- 第8章 面向对象编程(中级部分)
- 第10章 面向对象编程(高级部分)
目录
第8章 面向对象编程(中级部分)
8.1 面向对象编程三大特征
8.1.1 基本介绍
面向对象编程有三大特征:封装、继承和多态。
8.1.2 封装介绍
8.1.3 封装的理解和好处
8.1.4 封装的实现步骤 (三步)
8.1.5 测试代码
public class test5 {
public static void main(String[] args) {
//调用无参构造,分别设置各个属性来获取值
Account account1 = new Account();
account1.setName("张三");
account1.setAge(12);
account1.setSex("男");
System.out.println(account1.toString());
System.out.println("====");
//通过有参构造直接设置各个属性来获取值
Account account2 = new Account("李四",25,"女");
System.out.println(account2.toString());
}
}
8.2 面向对象编程-继承
8.2.1 为什么需要继承
继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。画出继承的示意图。
8.2.2 继承的基本语法
8.2.3 继承给编程带来的便利
1) 代码的复用性提高了
2) 代码的扩展性和维护性提高了
8.2.4 继承的深入讨论/细节问题
1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2) 子类必须调用父类的构造器, 完成父类的初始化
3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]
4) 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7) java 所有类都是 Object 类的子类, Object 是所有类的基类. 8) 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
9) 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
10) 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
8.3 super 关键字
8.3.1 基本介绍
super 代表父类的引用,用于访问父类的属性、方法、构造器。
8.3.2 基本语法
8.3.3 super 给编程带来的便利/细节
8.3.4 super 和 this 的比较
8.4 方法重写/覆盖(override)
8.4.1 基本介绍
8.4.2 注意事项和使用细节
8.5 面向对象编程-多态
8.5.1 多[多种]态[状态]基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
8.5.2 对象的多态 (核心,困难,重点)
8.5.3 多态注意事项和细节讨论
8.6 Object 类详解
8.6.1 equals 方法
8.6.2 如何重写 equals 方法
应用实例: 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。
//重写 Object 的 equals 方法
public boolean equals(Object obj) {
//判断如果比较的两个对象是同一个对象,则直接返回 true
if(this == obj) {
return true;
}
//类型判断
if(obj instanceof Person) {//是 Person,我们才比较
//进行 向下转型, 因为我需要得到 obj 的 各个属性
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
//如果不是 Person ,则直接返回 false
return false;
}
8.6.3 hashCode 方法
老韩的 6 个小结:
1) 提高具有哈希结构的容器的效率!
2) 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
3) 两个引用,如果指向的是不同对象,则哈希值是不一样的
4) 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址。5) 案例演示[HashCode_.java]: obj.hashCode() [测试:A obj1 = new A(); A obj2 = new A(); A obj3 = obj1]
6) 后面在集合,中 hashCode 如果需要的话,也会重写, 在讲解集合时,老韩在说如何重写 hashCode()。
8.6.4 toString 方法
1) 基本介绍
默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】
子类往往重写 toString 方法,用于返回对象的属性信息
2) 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式3) 当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用
public String toString() { //重写后,一般是把对象的属性值输出,当然程序员也可以自己定制
return "Monster{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
"};
8.6.5 finalize 方法
1) 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作【演示】
2) 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法。
3) 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制。
老韩提示: 我们在实际开发中,几乎不会运用 finalize , 所以更多就是为了应付面试.
第10章 面向对象编程(高级部分)
10.1 类变量内存布局
10.1.1 static关键字
10.1.2 什么是类变量
10.1.3 如何定义类变量
10.1.4 如何访问类变量
10.1.5 类变量使用注意事项和细节讨论
10.1.6 类方法基本介绍
10.1.7 类方法的调用
10.1.8 类方法使用注意事项和细节讨论
10.2 理解 main 方法语法
10.2.1 深入理解 main 方法
特别提示:
1) 在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。
2) 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
10.3 代码块
10.3.1 基本介绍
10.3.2 基本语法
10.3.3 代码块的好处
//老韩解读
//(1) 下面的三个构造器都有相同的语句
//(2) 这样代码看起来比较冗余
//(3) 这时我们可以把相同的语句,放入到一个代码块中,即可
//(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
//(5) 代码块调用的顺序优先于构造器..
{
System.out.println("电影屏幕打开...");
System.out.println("广告开始...");
System.out.println("电影正是开始...");
};
10.3.4 代码块使用注意事项和细节讨论
10.4 final 关键字
10.4.1 基本介绍
10.4.2 final 使用注意事项和细节讨论
10.5 抽象类
10.5.1 抽象类的介绍
10.5.2 抽象类使用的注意事项和细节讨论
10.5.3 解决之道-抽象类快速入门’
10.6 接口
10.6.1 基本介绍
10.6.2 案例
10.6.3 注意事项和细节
10.6.4 实现接口 vs 继承类
10.7 常用类
10.7.1 内部类
成员内部类、静态内部类、局部内部类、匿名内部类
特点:
- 编译之后可生成独立的字节码文件
- 内部类可直接访问外部类私有成员,而不破坏封装
- 可为外部类提供必要的内部功能组件
// 身体
class Body{
// 头部
class Header{
// 也会生成class文件
}
}
10.7.2 成员内部类
- 在类的内部定义,与实例变量、实例方法同级别的类
- 外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象
- 当外部类、内部类存在重名属性时,会优先访问内部类属性
- 成员内部类里不能定义静态成员、可以包含静态常量(final)
// 外部类
public class Outer{
//实例变量
private String name = "张三";
private int age = 20;
//内部类
class Inner{
private String address = "北京";
private String phone = "110";
private String name = "李四";
//方法
public void show(){
//打印外部类属性 此时有重名属性name
sout(Outer.this.name); // 张三
sout(age);
//打印内部类中的属性
sout(name); // 李四
sout(address);
sout(phone);
}
}
}
// 测试类
public class Test{
psvm(String[] args){
// 创建外部类对象
Outer outer = new Outer();
// 创建内部类对象
Inner inner = outer.new Inner();
//一步到位
Inner inner = new Outer(.new Inner();
inner.show();
}
}
10.7.3 静态内部类
不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。
// 外部类
public class Outer{
//实例变量
private String name = "xxx";
private int age = 20;
// 静态内部类,和外部类相同
static class Inner{
private String address = "上海";
private String phone = "111";
// 静态成员
private static int count = 1000;
//方法
public void show(){
// 调用外部类的属性
// 1. 先创建外部类对象
Outer outer = new Outer();
// 2. 调用外部类对象的属性
sout(outer.name);
sout(outer.age);
// 调用静态内部类的属性和方法
sout(address);
sout(phone);
// 调用静态内部类的静态属性
sout(Inner.count);
}
}
}
// 测试类
public class Test{
psvm(String[] args){
// 直接创建静态内部类对象
Outer.Inner inner = new Outer.Inner();
inner.show();
}
}
10.7.4 局部内部类
- 定义在外部类方法中,作用范围和创建对象范围仅限于当前方法
- 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final
- 限制类的使用范围
// 外部类
public class Outer{
//实例变量
private String name = "刘德华";
private int age = 35;
//方法
public void show(){
// 定义局部变量
String address = "sz";
// 局部内部类:注意不能加任何访问修饰符
class Inner{
private String phone = "11234";
private String email = "ldh@qq.com";
public void show2(){
// 访问外部类的属性
sout(name); // 相当于 Outer.this.name
sout(age);
// 访问内部类的属性
sout(this.phone);
sout(this.email);
// 访问局部变量 jdk1.7要求必须常量final、jdk1.8自动添加final
}
}
// 创建局部内部类对象
Inner inner = new Inner();
inner.show2();
}
}
// 测试类
public class Test{
psvm(String[] args){
// 创建外部类对象
Outer outer = new Outer();
outer.show();
}
}
10.7.5 匿名内部类
- 没有类名的局部内部类(一切特征都与局部内部类相同)
- 必须继承一个父类或者实现一个接口
- 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象
- 优点:减少代码量
- 缺点可读性较差
// 使用匿名内部类优化(相当于创建了一个局部内部类)
Usb usb = new Usb(){ // Usb为一个接口
@Override
public void service(){
sout("连接电脑成功,fan开始工作")
}
};
usb.service();
10.7.6 Object类
- 超类、基类,所有类的直接或间接父类,位于继承树的最顶层
- 任何类,如没有书写extends显示继承某个类,都默认直接继承Object类,否则为间接继承
- Object类中所定义的方法,是所有对象都具备的方法
- Object类型可以存储任何对象
- 作为参数,可接受任何对象
- 作为返回值,可返回任何对象
10.7.8 getClass()方法
public final Class<?> getClass(){}
- 返回引用中存储的实际对象类型
- 应用:通常用于判断两个引用中实际存储对象类型是否一致
// 判断s1 和 s2是不是同一个类型
Class class1 = s1.getClass();
Class class2 = s2.getClass();
// getClass返回 class类型
10.7.9 hashCode()方法
public int hashCode(){}
- 返回该对象的哈希码值
- 哈希值根据对象的地址或字符串或数字使用hash算法计算出来的int类型的值
- 一般情况下相同对象返回相同哈希码
s1.hashCode();
s2.hashCode();
// 自然不同
Student s3 = s1; // 此时s3的hashCode与s1相同
10.7.10 toString()方法
public String toSring(){}
- 返回该对象的字符串表示(表现形式)
- 可以根据程序需求覆盖该方法,如:展示对象各个属性值
sout(s1.toString()); // 直接打印包+类名+哈希值
// 重写 alt + enter + s
@override
public String toString(){
return "Student [name = " + name + ", age = " + age + "]";
}
10.7.11 equals()方法
public boolean equals(Object obj){}
- 默认实现为(this == obj), 比较两个对象地址是否相同
- 可进行覆盖,比较两个对象的内容是否相同
// 判断两个对象是否相等
sout(s1.equals(s2)); // false
Student s4 = new Strudent("小明", 17);
Student s5 = new Strudent("小明", 17);
sout(s4.equals(s5)); // false 堆中地址不同
// 重写 改变其比较内容
/*
步骤 1. 比较两个应用是否指向同一个对象
2. 判断obj是否为null
3. 判断两个引用只想的实际对象类型是否一致
4. 强制类型转换
5. 依次比较各个属性值是否相同
*/
@override
public boolean equals(Object obj){
// 1.
if(this == obj){
return true;
}
// 2.
if(obj == null){
return false;
}
// 3.
// if(this.getClass() == obj.getClass()){
//
// }
// instanceof 判断对象是否是某种类型
if(obj instanceof Student){
// 4.强制类型转换
Student s = (Student)obj;
// 5. 比较属性
if(this.name.equals(s.getName()) && this.age == s.getAge()){
return true;
}
}
return false;
}
10.7.12 finalize()方法
- 当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列
- 垃圾对象:没有有效引用指向此对象时,为垃圾对象
- 垃圾回收:由gc销毁垃圾对象,释放数据存储空间
- 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象
- 手动回收机制:使用
System.gc();
通知JVM执行垃圾回收
@Override
protected void finalize() throws Throwable{
sout(this.name + "对象被回收了");
}
psvm(String[] args){
Student s1 = new Student("aaa", 29); // 不是垃圾
new Student("bbb", 30); // 是辣鸡 会被回收
//回收垃圾
System.gc();
sout("回收垃圾");
// 打印出 “回收垃圾
// aaa对象被回收了”
}