文章目录
面向对象编程 (上)
Java网课:韩顺平 零基础30天学会Java
1. 封装
封装的实现步骤
- 将属性进行私有化 private【不能直接修改属性】
- 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(/*类型 参数名*/){ // Xxx表示某个属性
// 加入数据验证的业务逻辑
// 属性 = 参数名;
}
- 提供一个公共的get方法,用于获取属性的值
public XX getXxx(){ // 权限判断
return xx;
}
2. 继承
1. 继承的基本注意事项
- 子类继承了所有的属性和方法,非私有的属性和方法可以直接访向,但是私有属性和方法不能在子类直接访向,要通过公共的方法去访问
- 子类必须调用父类的构造器完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参枃造器:则必须在子类的构造器中用 super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
- 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
- super在使用时,需要放在构造器第一行
- super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器( this() 和 super() 都是在类中的构造器中使用,this() 是调用类本身的另一个构造器,super()是调用父类的构造器,this不是作为构造器的时候则可以和super()共用)
- java所有类都是 Object类的子类,Object是所有类的基类
- 父类构造器的调用不限于直接父类!将一直往上追溯到 Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。 思考:如何让A类继承B类和C类?[A继承B,B继承C]
- 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
Person is a Music?
Person Music
Music extends Person //不合理
Anima
Cat extents Animal //合理
2. this和super
No. | 区别 | this | super |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 直接访问父类中的方法 |
3 | 调用构造器(二选一) | 调用本类构造器,必须放在构造器的首行 | 调用父类枃造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
3. 方法重写(方法覆盖)
- 子类的方法的 参数、方法名称 要和父类方法的参数方法名称完全一样。
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 子类方法的访问权限 ≥ 父类方法的访向权限
4. 方法重写与重载
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载(overload) | 本类 | 必须一样 | 类型、个数、顺序至少有一个不同 | 无要求 | 无要求 |
重写(override) | 父子类 | 必须一样 | 必须相同 | 子类重写方法返回类型和父类方法返回类型一致,或者为父类方法返回类型的子类 | 子类方法的访问修饰符 ≥ 父类的访问修饰符 |
3. 多态
1. 基本概念
-
方法的多态——方法重载的多态 + 方法重写的多态
-
对象的多态(核心,困难,重点)
-
一个对象的编译类型和运行类型可以不一致
-
编译类型在定义对象时,就确定了,不能改变
-
运行类型是可以变化的
-
编译类型看定义时 = 号的左边,运行类型看 = 号的右边
案例:
Animal animal= new Dog();【animal编译类型是 Animal,运行类型Dog】
animal= new Cat(); 【animal的运行类型变成了Cat,编译类型仍然是 Animal】
-
2. 多态的向上转型
父类引用指向子类对象本质:父类的引用指向了子类的对象
语法: 父类类型 引用名 = new 子类类型();
特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员(需遵守访向权限)
不能调用子类中特有成员,因为在编译阶段,能调用哪些成员,是由编译类型来决定的
最终运行效果看子类的具体实现!
3. 多态的向下转型
语法: 子类类型 引用名 = (子类类型) 父类引用;
只能强转父类的引用,不能强转父类的对象
要求父类的引用必须指向的是当前目标类型的对象,即父类引用本来就是指向要强转的子类类型的对象的
当向下转型后,可以调用子类类型中所有的成员
编译类型决定可以调用的范围,运行类型决定方法最终的执行效果
属性没有重写之说,属性的值直接看编译类型
instanceOf比较操作符,用于判断对象的运行类型是否为X类型成XX类型的子类型
4. java的动态绑定机制
1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用提示:看到继承关系,就要联想到动态绑定的问题
5. 多态的应用
1. 多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
public class PolyArray {
public static void main(String[] args) {
//Teacher和Student是Person的子类
Person[] people = new Person[5];
people[0] = new Person("wjh", 16);
people[1] = new Student("wjh", 17, 646);
people[2] = new Student("xdd", 17, 630);
people[3] = new Teacher("jls", 47, 8000);
people[4] = new Teacher("yyg", 36, 7000);
for (int i = 0; i < people.length; i++) {
//people[i]编译类型是Person,运行类型根据实际情况由JVM来判断
System.out.println(people[i].say());//动态绑定
//类型判断 + 向下转型,这样就可以调用子类独有的方法了
if (people[i] instanceof Teacher) {
((Teacher) people[i]).teach();
} else if (people[i] instanceof Student) {
((Student) people[i]).study();
} else if (people[i] instanceof Person) {
} else {
System.out.println("你的类型有误,请自己检查...");
}
}
}
}
2. 多态参数
public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("Tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
PolyParameter polyParameter = new PolyParameter();
polyParameter.showEmpAnnual(tom);
polyParameter.showEmpAnnual(milan);
polyParameter.testWork(tom);
polyParameter.testWork(milan);
}
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual()); //动态绑定机制
}
//多态参数
public void testWork(Employee e) {
if (e instanceof Worker) { //类型判断
((Worker) e).work(); //向下转型
} else if (e instanceof Manager) {
((Manager) e).manager();
} else {
System.out.println("不做处理");
}
}
}
4. Object类详解
1. == 和 equals() 的区别
== 是一个比较运算符
-
== : 既可以判断基本类型,又可以判断引用类型
-
== : 如果判断基本类型,判断的是值是否相等
示例:int i=19; double d=10.0;
-
如果判断引用类型,判断的是地址是否相等。即判定是不是同一个对象
equals()
- equals : 是 Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
2. hashCode()
- 提高具有哈希结构的容器的效率!
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的!不能完全将哈希值等价于地址
- 后面在集合,中 hash Code如果需要的话,也会重写
由类别Object定义的hashCode方法确实会针对不同对象返回不同的整数。这通常通过将对象的内部地址转换为整数来实现,但Java的编程语言不需要此实现技术。
提示:对象的内部地址即实际的地址,由于Java是运行再虚拟机上的,因此我们不能拿到对象的内部地址。与之相对的是,C/C++则可以取得实际的地址
3. toString()
-
默认返回:全类名 + @ + 哈希值的十六进制 [全类名 即包名 + 类名]
-
子类往往重写 toString(),用于返回对象的属性信息
-
重写toString(),打印对象就拼接对象时,都会自动调用该对象的 toString形式 【idea快捷键:alt + insert】
-
当直接输出一个对象时, toString()会被默认的调用
System.out.println(monster)
<=>System.out.println(monster.toString)
4. finalize()
- 当对象被回收时,系统自动调用该对象的 finalize()。子类可以重写该方法做一些释放资源的操作;
- 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize();
- 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过 System.gc() 主动触发垃圾回收机制(不一定触发成功)。
提示:实际开发中,几乎不会运用 finalize(),所以主要是为了应付面试
public class Finalize_ {
public static void main(String[] args) {
Car car = new Car("宝马");
//执行到 car = null; 时,car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象
//在销毁对象前,会调用该对象的 finalize()
//程序员就可以在 finalize() 中,写自己的业务逻辑代码,比如释放资源:数据库连接、或者打开的文件...
//如果程序员不重写finalize(),那么就调用 Object类的 finalize(),即默认处理
//如果程序员重写finalize(),那么就可以实现自己的逻辑
car = null;
System.gc(); //主动调用垃圾回收器,不一定触发成功
System.out.println("程序退出了...");
}
}
class Car {
private String name;
public Car(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁汽车:" + name);
System.out.println("我们释放了某些资源...");
// super.finalize();
}
}
//程序输出如下,由于System.gc()调用后,程序不会阻塞在这里,因此不会是简单的顺序输出
/*
程序退出了...
我们销毁汽车:宝马
我们释放了某些资源...
*/
5. 问答
问题一:
什么是多态,多态具体体现有哪些?(可举例说明)
多态:方法就对象具有多种形态,是OOP的第三大特征,是建立在封装和继承基础之上。
多态具体体现
1、方法多态
(1)重载体现多态 (2)重写体现多态
2、对象多态
1)对象的编译类型和运行类型可以不一致,编译类型在定义时,就确定了不能变化
2)对象的运行类型是可以变化的,可以通过 getClass()来查看运行类型
3)编译类型看定时时 = 号的左边,运行类型看 = 号右边
问题二:
java的动态绑定机制是什么?
1、当调用对象的方法时,该方法会和对象的内存地址/运行类型绑定
2、当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
问题三:
== 和 equals() 的区别是什么?
一、== 是一个比较运算符
- == : 既可以判断基本类型,又可以判断引用类型
- == : 如果判断基本类型,判断的是值是否相等
示例:int i=19; double d=10.0; - 如果判断引用类型,判断的是地址是否相等。即判定是不是同一个对象
二、equals()
- equals : 是 Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等