Java基础之面向对象详解
#####################################
- 基本概念
- 类与对象
- 构造方法及其重载
- static修饰符
- 封装
- 继承
- 抽象
- final修饰符
- 多态
- instanceof运算符
- this指针
#####################################
基本概念
面向对象可以说是采用现实模拟的方式来设计和开发程序。
比如:1岁的鹦鹉正在说话。
那么面向对象的理解就是:需要一只鹦鹉,它具有健康的属性,说话的行为,它现在在说话。
程序代码如下:
public class Demo {
public static void main(String[] args) {
YingWu yingWu=new YingWu();//创建一个鹦鹉对象
yingWu.talk();//调用鹦鹉说话的方法
}
}
//鹦鹉
class YingWu {
public String name="鹦鹉小绿";//名字 属性
public Integer health=100;//健康 属性 没用到
public Integer age=1;//年龄 属性
//鹦鹉所具有的行为
//飞
public void fly(){
System.out.println("正在飞");
}
//会说话
public void talk(){
System.out.println(age+"岁的"+name+"正在说话");
}
}
可以很简单就和面向过程区分开,我举例:
举办一场魔术表演
面向过程:主持人宣布开始---演员小明表演---结束表演
面向对象:我们先分析表演需要什么类?主持人,魔术师,道具等等。分析每个类的行为,主持人讲话,过场;魔术师变魔术;不同的道具也具有不同的作用(可以看作行为)。
显而易见,
面向过程的中心是功能。
而面向对象技术利用面向对象的思想描述世界,每个对象都拥有自己的属性和行为。我们创建对象,调用对象的行为(方法)。
面向对象的过程就是抽象的过程,。把现实生活中的物,抽象成软件程序中的“对象”。
类与对象
类是对某一类事物的描述,可以说是一个模板,拥有属性和行为。类是抽象的,概念上的定义。
对象是实际存在的,具体的事物个体。
比如创建一个类:人;根据这个人的类创建出小明这个对象。
Person xiaoMing=new Person();//Person代表人 类,xiaoMing是依据类 创建 的对象,也称为实例
构造方法及其重载
构造方法的名称和它的类名相同,没有返回值。
//鹦鹉
class YingWu {
//构造方法
public YingWu(){
System.out.println("执行无参构造");
name="小绿";
health=60;
age=2;
}
构造方法在我们创建对象时执行,执行一些初始化操作,比如给属性赋值。
那如果这个类我看不到构造方法,是怎么回事?
//鹦鹉
class YingWu {
//没看到构造方法
public String name = "鹦鹉小绿";//名字 属性
public Integer health = 100;//健康 属性 没用到
public Integer age = 1;//年龄 属性
//鹦鹉所具有的行为
//飞
public void fly() {
System.out.println("正在飞");
}
//会说话
public void talk() {
System.out.println(age + "岁的" + name + "正在说话");
}
}
看不到,不是没有,而是藏起来了。我们称他为隐式构造方法。
当我们构建一个类,没有写构造方法时,系统会提供一个空的构造方法(隐式构造方法)。
public class Demo {
public static void main(String[] args) {
YingWu yingWu=new YingWu();
yingWu.talk();
}
}
//鹦鹉
class YingWu {
//构造方法
public YingWu(){
System.out.println("执行无参构造");
name="小粉";
age=2;
}
public String name = "鹦鹉小绿";//名字 属性
public Integer health = 100;//健康 属性 没用到
public Integer age = 1;//年龄 属性
//鹦鹉所具有的行为
//飞
public void fly() {
System.out.println("正在飞");
}
//会说话
public void talk() {
System.out.println(age + "岁的" + name + "正在说话");
}
}
执行结果:
执行无参构造
2岁的小粉正在说话
我们虽然通过构造方法完成了对象属性的赋值,但是属性值是在构造方法中固定的,我们如何在创建对象时为属性动态赋值呢?
我们
可以通过带参构造方法解决这个问题。
public class Demo {
public static void main(String[] args) {
YingWu yingWu1=new YingWu();
YingWu yingWu2=new YingWu("小黄",3);
yingWu2.talk();
}
}
//鹦鹉
class YingWu {
//无参构造方法
public YingWu(){
System.out.println("执行无参构造");
name="小粉";
age=2;
}
//有参构造方法
public YingWu(String name,Integer age){
System.out.println("执行有参构造");
this.name=name;
this.age=age;
}
在一个类中,方法名相同,参数列表(方法的参数个数或参数类型)不同,我们称为重载。
重载和方法访问修饰符,以及返回值无关。
我们 可以通过构造方法的重载来实现多种初始化行为,在创建对象时可以根据需要选择合适的构造方法。
如果我们此时将无参构造注释掉,只留下有参构造。在YingWu yingWu1=new YingWu();处会编译报错。告诉你是new YingWu()因为该构造未定义。
之前不是说有个隐式的构造吗?
是这样的,java在构建类时,我们没有写上构造方法,系统会给我们提供一个隐式无参构造。
但是当我们显式地写了自定义的构造方法,系统就不会给我们提供隐式无参构造。想要,自己写一个。
static修饰符
static可以用来修饰属性,方法和代码块。static修饰的变量属于这个类所有。即由这个类创建的所有对象共同用一个static变量。通常把static修饰的属性和方法称为类属性和类方法。
不使用static修饰的属性和方法属于单个对象,通常称为实例属性和实例方法。
可以看下static的执行顺序,如下:
public class StaticDemo {
// 执行顺序 1 2 3 。。。
static int i = 1;//1
static int m = 30;//2
int j;//6
int k = 25;//7
static {
i = 10;//3
System.out.println("i的初始化值为:" + i);//4
}
public StaticDemo() {
j = 20;//8
System.out.println("j的初始化值为:" + i);//9
}
public static void getNum() {
System.out.println("得到i的值为:" + i);//12
}
public static void main(String[] args) {
StaticDemo staticDemo = new StaticDemo();//5
System.out.println("i的值为:" + StaticDemo.i);//10
staticDemo.getNum();//11
System.out.println("m的值为:" + staticDemo.m);//13
System.out.println("k的值为:" + staticDemo.k);//14
}
}
得出结论:
在类加载的过程中,先分配好静态变量,再执行静态代码块,这些都是在创建对象之前执行的。
类属性和类方法可以通过类名和对象名访问,实例属性和实例方法只能通过对象名访问。
类方法只能访问类属性和其他类方法。
实际上,static变量和方法的主要用途就是在没有创建对象的前提下,仅通过类本身调用;
在类加载时候初始化,常驻在内存中,调用快捷方便。
Java在静态方法中不能使用this、super。
在静态方法内部不能调用非静态方法,反过来是可以的。
在方法中不可以定义static变量。
面向对象三大特征
封装 继承 多态
封装
封装就是将类中的属性声明为私有的,同时提供公有的方法实现对该成员的存取操作。
好处是,隐藏了类的实现细节 (对外提供接口,外面不能随意获取该类中隐私数据),让使用者只能通过规定的方式获取数据(通过getter获取数据),保证数据的安全性。
继承
Java只支持单继承(一个类只能有一个直接的父类)。当然,java类的根父类是java.lang.Object。
类的修饰符如果是public,代表该类在整个项目下可见。若没有修饰符,代表该类只能在当前包下可见。不可以使用private和protected修饰类。
子类可以重写父类的方法,重写需满足以下要求:
重写方法和被重写方法的方法名必须一致。
重写方法和被重写方法的参数列表必须一致。
重写方法的返回值类型必须和被重写方法的返回值类型一致或是其子类。
重写方法的访问修饰符不能严于被重写方法的访问修饰符。
继承条件下构造方法的调用规则如下:
子类构造方法如果没有通过super显示调用父类的有参构造函数,并且没有通过this显示调用该类其它的构造方法,会默认先调用父类的无参构造函数。
在构造方法中,this语句和super语句只能放在第一条。
一个构造方法内,不能同时出现this和super语句。
静态方法(类方法)不允许出现this,super关键字。(静态方法和对象无关,类即可调用 )
抽象
有时候,有些父类我们不会去实例对象,这个类存在的意义只是为了提供一个模板,供子类使用。这时,我们可以使用Java中的抽象类来实现,用abstract来修饰。抽象类不能实例化。
抽象类提供的方法,如何让子类强制重写呢?
用abstract关键字修饰抽象类的方法可以实现,我们称为抽象方法。
要注意,抽象类中,抽象方法没有方法体(大括号)。非抽象类不能有抽象方法。
抽象类有构造方法。
构造方法不能被继承。
final修饰符
如果想让一个类不被其它类继承,就为这个类加上final。
如果想让一个方法不被子类重写,就为这个方法加上final。
如果想让一个属性的值不允许改变,就为这个属性加上final。
abstract是否可以和private、static、final共用?
1.abstract的作用说白了就是让子类继承的,让方法重写的,加个private,子类都访问不到了。
2.static的作用就是直接让类访问方法和属性,abstract只能用于修饰类和方法,作用是让子类继承和方法重写。abstract修饰的方法没有方法体。如果两者共用,你用类调用一个没有方法体的抽象方法,明显有问题。
3.final是最终的意思,可以使类不被继承,方法不被重写,属性不被改变值。abstract修饰的类可以被继承,修饰的方法必须重写。两者矛盾。
final修饰符可以修饰引用变量吗?
final Dog dog=new Dog("小明","接飞盘");
dog.setHabbit("跨栏");
dog=new Dog("小红","啥也不会");
当final修饰引用型变量(dog)时,dog变量(对象)无法在指向另外一个对象(重新创建对象),该变量值无法改变。
但是引用型变量的属性值是可以改变的。
思考一个问题,用final修饰类,类里的方法可以重写吗?
多态
多态是具有表现多种形态的能力的特征。同一个实现接口,使用不同的实例可以执行不同的操作。
解释如下:
//子类到父类的转换,向上转型
Pet dog = new Dog( );//Pet引用变量指向dog实例 狗
Pet penguin = new Penguin();//Pet引用变量指向penguin实例 企鹅
//子类重写父类实例接口,调用之
dog.info();
penguin.info();
// 控制台输出结果:
// 狗狗喜欢接飞盘
// 企鹅喜欢吃鱼
使用多态的主要方式:
1. 使用父类作为方法形参实现多态。
做法:修改Person类,注释play(Penguin penguin)和play(Dog dog)方法,新增play(Pet pet)方法。
public class Demo {
public static void main(String[] args) {
//狗
Dog dog = new Dog();//Pet引用变量指向dog实例
//企鹅
Penguin penguin = new Penguin();//Pet引用变量指向penguin实例
//饲养员
Person person = new Person();
person.play(dog);//饲养员陪狗玩
person.play(penguin);//饲养员陪企鹅玩
// 控制台输出结果
// 狗狗接飞盘
// 企鹅吃鱼
}
}
class Person {
//陪企鹅玩企鹅方法
public void play(Penguin penguin) {
penguin.info();
}
//陪狗玩方法
public void play(Dog dog) {
dog.info();
}
}
修改之后,如下:
public class Demo {
public static void main(String[] args) {
//狗
Dog dog = new Dog();//Pet引用变量指向dog实例
//企鹅
Penguin penguin = new Penguin();//Pet引用变量指向penguin实例
//饲养员
Person person = new Person();
person.play(dog);//饲养员陪狗玩
person.play(penguin);//饲养员陪企鹅玩
// 控制台输出结果
// 狗狗接飞盘
// 企鹅吃鱼
}
}
class Person {
//陪宠物玩
public void play(Pet pet) {
pet.info();
}
// 陪企鹅玩企鹅方法
// public void play(Penguin penguin) {
// penguin.info();
// }
// 陪狗玩方法
// public void play(Dog dog) {
// dog.info();
// }
看出,两者输出结果没有区别。区别只是修改后代码量更少了,提高了代码的可扩展性和可维护性。
2. 使用父类作为方法返回值实现多态
举例:
为Person类添加购买方法,实现购买宠物功能
购买方法返回值是Pet
我们调用宠物吃的方法,来看下返回值Pet是什么宠物
public class Demo {
public static void main(String[] args) {
//购买者
Person person = new Person();
System.out.println("欢迎来到亲子宠物店");
System.out.println("您要购买什么宠物?狗?企鹅?");
//调用买的方法
Pet pet = person.buy("狗");
//给宠物喂点吃的
pet.eat();
////////////////////////////////////////////////////
System.out.println("请问还要买什么宠物?");
//调用买的方法
Pet pet2 = person.buy("企鹅");
//给宠物喂点吃的
pet2.eat();
}
}
class Person {
public Pet buy(String name) {
Pet pet = null;
if ("狗".equals(name)) {
return new Dog();
} else if ("企鹅".equals(name)) {
return new Penguin();
} else {
return pet;
}
}
}
总结多态三个条件
多态以继承为基础。没有继承,就没有多态。
子类重写父类的方法,多态情况下会调用子类重写的方法
父类引用变量指向子类对象(子类向父类转换,向上转型)
instanceof运算符
既然有向上转型,那么就有向下转型
父类向子类转型可以说是向下转型。
//向上转型 为Pet对象
Pet pet=new Dog();
//向下转型 为狗对象
Dog dog= (Dog) pet;
//向下转型 为企鹅对象
Penguin penguin= (Penguin) pet;//强制转换异常 java.lang.ClassCastException
在向下转型时,如果没有转换为正确的子类类型,会报出强制转换异常。可以用instanceof运算符判断,避免异常,提升代码健壮性。
语法:
对象 instanceof 类
//向上转型 为Pet对象
Pet pet = new Dog();
//向下转型 为企鹅对象
if (pet instanceof Penguin) {
//向下转型 为狗对象
Dog dog = (Dog) pet;
}
if (pet instanceof Penguin) {
Penguin penguin = (Penguin) pet;
}
this指针
在java中,this是一个引用当前类的对象的引用变量。一共有6中用法
- 可以用来引用当前类的实例变量(普通属性,非静态属性)
- 可以用来调用当前类的方法,this可以隐藏不写。
- this()可以用来调用当前类的构造函数,this()只能在构造函数中第一行写。
- 可以用作方法调用中的参数传递。
- 可以用作构造方法调用中的参数传递。
- 可以用于从方法返回当前实例,如下。
public Pet a(){ return this; }