Java笔记7 面向对象编程(中级)

7.1 包

包的本质实际上就是创建不同的文件夹/目录来保存类文件

7.1.1 命名规则

  • 只能包含数字、字母、下划线、小圆点.,但是不能用数字开头,不能是关键字或保留字
  • com.公司名.项目名.业务模块名

7.1.2 常用的包

  • java.lang.* //lang是默认的包,默认引入,不需要再引入
  • java.util.* //util包,系统提供的工具包,工具类
  • java.net.* //网络包,网络开发
  • java.awt.* //java的界面开发,GUI

7.2 访问修饰符

7.2.1 基本介绍

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  • 公开级别:用 public 修饰,对外公开
  • 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
  • 默认级别:没有修饰符号,向同一个包的类公开.
  • 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.

7.2.2 四种访问修饰符的访问范围

在这里插入图片描述

7.2.3 注意事项

  1. 修饰符可以用来修饰类中的属性、成员方法、类
  2. 只有public和默认才能修饰类,并遵守上述访问权限的特点
  3. 子类中的访问权限放在继承章节
  4. 成员方法的访问规则和属性一样

7.3 面向对象编程三大特征之封装

7.3.1 介绍

封装(encapsulation)就是把抽象出来的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作。

7.3.2 封装的理解

  • 隐藏实现细节:方法(连接数据库) <== 调用(传入参数…)

  • 可以对数据进行验证,保证安全合理

7.3.3 封装的实现步骤(三步)

  • 将属性进行私有化private 【不能直接修改属性】
  • 提供一个公共的set方法
  • 提供一个公共的get方法

7.3.4 构造器与set方法结合

  • 最好在构造器中用set方法修改属性

7.4 面向对象编程三大特征之继承

7.4.1 介绍

  • 继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可

  • 父类又叫超类、基类;子类又叫派生类

  • 继承示意图在这里插入图片描述

  • 使用继承可以提高代码的复用性、扩展性以及可维护性

7.4.2 使用细节!

  • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类中直接访问,但是私有的属性和方法不能在子类中直接访问,要通过父类提供的公共的方法去访问
  • 子类必须调用父类的构造器,完成父类的初始化
  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器(执行了super();),如果父类没有提供无参构造器,则必须在子类中的构造器中用super(参数列表);去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
  • 如果希望指定去调用父类的某个构造器,则显式地调用一下:super(参数列表);
  • super();在使用时,必须放在构造器的第一行(super();只能在构造器中使用)
  • super();this();都是只能放在构造器的第一行,因此这两个方法不能共存在同一个构造器
  • Java的所有类都是Object类的子类,Object类是所有类的基类
  • 父类构造器的调用不限于直接父类,将一直往上追溯到Object类(顶级父类)
  • 子类最多只能继承一个父类(直接父类),即Java中是单继承机制
  • 不能滥用继承,子类和父类直接必须满足is-a的逻辑

7.4.3 继承的本质分析!

  • 依次加载Object类、GrandPa类、Father类、Son类
  • son访问属性时,要按照查找关系来返回信息
    • 首先看子类的属性,是否有该属性
    • 如果子类有该属性且可以访问,则返回信息
    • 如果子类没有该属性,就看父类有没有这个属性(如果父类有该属性且可访问就返回信息)
    • 如果父类没有就继续往上找,知道Object,都没有接报错,有就返回
    • 父类中不能访问的属性可以通过父类提供的公共方法访问
    • 父类中的属性时私有的,该属性在父类的父类中是公有的,子类也不能直接访问,访问到父类发现是私有的直接报错
  • 在这里插入图片描述

7.4.4 继承练习

public class Test {
    public static void main(String[] args) {
        C c = new C();
    }
}

class A {
    public A() {
        System.out.println("我是A类");
    }
}

class B extends A {
    public B() {
        System.out.println("我是B类的无参构造");
    }

    public B(String name) {
        System.out.println(name + "我是 B 类的有参构造");
    }
}

class C extends B { //C 类,继承 B 类
    public C() {
        this("hello");
        System.out.println("我是 c 类的无参构造");
    }

    public C(String name) {
        super("hahah");
        System.out.println("我是 c 类的有参构造");
    }
}
/*输出:
我是A类
hahah我是 B 类的有参构造
我是 c 类的有参构造
我是 c 类的无参构造
 */

7.5 super关键字

7.5.1 介绍

  • super代表父类的引用,用于访问父类的属性、方法、构造器

7.5.2 super 给编程带来的便利/细节

  • 调用父类构造器的好处

    • 分工明确,父类属性由父类初始化,子类属性由子类初始化
  • 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super;如果没有重名,使用super、this、直接访问是一样的效果

  • super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则,也需要遵守访问权限的规则

7.5.3 super和this的比较

区别点thissuper
访问属性访问本类中的属性,如果本类没有此属性则从父类中继续查找从父类开始查找属性
调用方法访问本类中的方法,如果本类没有此方法则从父类继续查找从父类开始查找方法
调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须放在子类构造器的首行
特殊表示当前对象子类中访问父类对象

7.6 方法重写/覆盖(override)

7.6.1 介绍

  • 方法覆盖(重写)就是子类中有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法

7.6.2 使用细节

  • 子类的方法的形参列表、方法名称要和父类的完全一样
  • 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
    • 比如父类方法的返回类型是Object,子类方法的返回类型是String
  • 子类方法不能缩小父类方法的访问权限
    • public > protected > 默认 >private

7.6.3 重写与重载的区别

名称发生范围方法名形参列表返回类型修饰符
重载(overload)本类必须一样类型、个数或者顺序至少有一个不同无要求无要求
重写(override)父子类必须一样相同子类重写方法,返回的类型和父类方法返回的类型一致,或者是其子类子类的方法不能缩小父类方法的访问范围

7.7 面向对象编程三大特征之多态

7.7.1 多种多态的介绍

方法或对象具有多种形态。是面向对象的三大特性之一,多态是建立在封装和继承基础之上的

7.7.1.1 方法的多态
public class Test {
    public static void main(String[] args) {
        //方法重载体现多态
        A a = new A();
        //这里我们传入不同的参数,就会得到不同的sum方法,体现了多态
        System.out.println(a.sum(10, 20));
        System.out.println(a.sum(10, 20, 30));

        //方法重写体现多态
        B b = new B();
        //不同的对象调用不同的say()方法,体现了多态
        a.say();
        b.say();
    }
}
class B {   //父类
    public void say() {
        System.out.println("B say()方法被调用...");
    }
}
class A extends B {
    public int sum(int n1, int n2) {
        return n1 + n2;
    }
    public int sum(int n1, int n2, int n3) {
        return n1 + n2 + n3;
    }

    public void say() {
        System.out.println("A say()方法被调用...");
    }
}
7.7.7.2 对象的多态

重要说明:

  1. 一个对象的编译类型和运行类型可以不一致

    1. Animal animal = new Dog();
    2. 父类的一个引用指向子类的一个对象
    3. animal编译类型是Animal,运行类型是Dog
  2. 编译类型在定义对象时,就确定了,不能改变

  3. 运行类型是可以变化的

    1. animal = new Cat();
    2. animal的运行类型变成了Cat,编译类型仍是Animal
  4. 编译类型看定义时 = 号的左边,运行类型看 = 号的右边

举例

public class Test {
    public static void main(String[] args) {
        //体验对象多态的特点
        Animal animal = new Dog();
        //编译类型:Animal,运行类型:Dog
        animal.cry();   //Dog cry() 小狗汪汪叫...

        animal = new Cat();
        //animal编译类型不变,运行类型变为Cat
        animal.cry();   //Cat cry() 小猫喵喵叫...
    }
}
class Animal {
    public void cry() {
        System.out.println("动物在叫...");
    }
}
class Cat extends Animal {
    @Override
    public void cry() {
        System.out.println("Cat cry() 小猫喵喵叫...");
    }
}
class Dog extends Animal {
    @Override
    public void cry() {
        System.out.println("Dog cry() 小狗汪汪叫...");
    }
}

7.7.2 入门案例

public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
}
public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
}
public class Food {
    private String name;

    public Food(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Fish extends Food {
    public Fish(String name) {
        super(name);
    }
}
public class Bone extends Food {
    public Bone(String name) {
        super(name);
    }
}
public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //使用多态机制,可以统一的管理主人喂食的问题
    //animal 编译类型是Animal,可以指向(接收) Animal子类的对象
    //food 编译类型是Food ,可以指向(接收) Food子类的对象
    public void feed(Animal animal, Food food) {
        System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());
    }

    //主人给小狗 喂食 骨头
//    public void feed(Dog dog, Bone bone) {
//        System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
//    }
//    //主人给 小猫喂 黄花鱼
//    public void feed(Cat cat, Fish fish) {
//        System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
//    }

    //如果动物很多,食物很多
    //===> feed 方法很多,不利于管理和维护
    //Pig --> Rice
    //Tiger ---> meat ...
    //...

}
public class Poly01 {
    public static void main(String[] args) {

        Master tom = new Master("汤姆");
        Dog dog = new Dog("大黄~");
        Bone bone = new Bone("大棒骨~");
        tom.feed(dog, bone);

        Cat cat = new Cat("小花猫~");
        Fish fish = new Fish("黄花鱼~");
        System.out.println("===========-------");
        tom.feed(cat, fish);

        //添加 给小猪为米饭
        Pig pig = new Pig("小花猪");
        Rice rice = new Rice("米饭");
        System.out.println("===================");
        tom.feed(pig, rice);
    }
}

7.7.3 多态注意事项

前提是:两个对象(类)存在继承关系

7.7.3.1 多态的向上转型

本质:父类的引用指向了子类的对象

语法:父类类型 引用名 = new 子类类型();

特点:

  • 编译类型看左边,运行类型看右边
  • 可以调用父类中的所有成员(需遵守访问规则)
  • 不能调用子类的特有成员
    • 因为在编译阶段,能调用哪些成员,是由编译类型确定的
  • 最终运行效果看子类的具体实现
    • 即调用方法时,从子类开始查找方法,然后调用,规则与前边写的方法调用规则一致
7.7.3.2 多态向上转型

语法:子类类型 引用名 = (子类类型)父类引用;

特点:

  • 只能强转父类的引用,不能强转父类的对象

  • 要求父类的引用必须指向的是当前目标类型的对象

  • 当向下转型后,可以调用子类类型中的所有成员

  • 属性没有重写之说!属性的值看编译类型

  • public class PolyDetail02 {
    	public static void main(String[] args) {
    		//属性没有重写之说!属性的值看编译类型
    		Base base = new Sub();//向上转型
    		System.out.println(base.count);// ? 看编译类型 10
    		Sub sub = new Sub();
    		System.out.println(sub.count);//? 20
    	}
    }
    class Base { //父类
    	int count = 10;//属性
    }
    class Sub extends Base {//子类
    	int count = 20;//属性
    }
    
  • instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

    • public class PolyDetail03 {
      	public static void main(String[] args) {
      		BB bb = new BB();
      		System.out.println(bb instanceof BB);// true
      		System.out.println(bb instanceof AA);// true
      		//aa 编译类型 AA, 运行类型是 BB
      		//BB 是 AA 子类
      		AA aa = new BB();
      		System.out.println(aa instanceof AA);//true
      		System.out.println(aa instanceof BB);//true
      		Object obj = new Object();
      		System.out.println(obj instanceof AA);//false
      		String str = "hello";
      		//System.out.println(str instanceof AA);
      		System.out.println(str instanceof Object);//true
      	}
      }
      class AA {} //父类
      class BB extends AA {}//子类
      

7.7.4 多态练习

public class PolyExer01 {
    public static void main(String[] args) {
        double d = 13.4;	//ok
        long l = (long)d;	//ok
        System.out.println(l);	//13
        int in = 5;	//ok
        boolean b = (boolean)in;	//error boolean -> int
        Object obj = "hello";	//ok 向上转型
        String objStr = (String)obj;	//ok  向下转型
        System.out.println(objStr);	//hello
        Object objPri = new Integer(5);	//ok 向上转型
        String str = (String)objPri;	//error 指向Integer的父类引用转成String
        Integer str1 = (Integer)objPri;	//ok 向下转型
    }
}
public class PolyExer02 {
    public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.count);	//20
        s.display();	//20
        Base b = s;
        System.out.println(b == s);	//true
        System.out.println(b.count);	//10
        b.display;	//20
    }
}
class Base {
    int count = 10;
    public void display() {
        System.out.println(this.count);
    }
}
class Sub extends Base {
    int count = 20;
    public void display() {
        System.out.println(this.count);
    }
}

7.7.5 Java的动态绑定机制 !!!

  1. 当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public class Test {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());    //B类的sum()注释前输出40,注释后输出30
        System.out.println(a.sum1());   //B类的sum1()注释前输出30,注释后输出20
    }
}

class A {
    public int i = 10;
    //当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
    //a的运行类型是B类,调用B类的getI()方法
    public int sum() {
        return getI() + 10;
    }
    //当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
    //此处的i是A类编译时声明的,所以值为10
    public int sum1() {
        return i + 10;
    }
    public int getI() {
        return i;
    }
}
class B extends A {
    public int i = 20;
    /*public int sum() {
        return i + 20;
    }*/
    public  int getI() {
        return i;
    }
    /*public int sum1() {
        return i + 10;
    }*/
}

7.7.6 多态的应用

7.7.6.1 多态数组
  • 介绍:

  • 数组的定义类型为父类类型,里边保存的实际元素类型为子类类型

  • 案例:

      1. 现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组 中,并调用每个对象 say 方法
      1. 应用实例升级:如何调用子类特有的方法,比如 Teacher 有一个 teach , Student 有一个 study 怎么调用?
    • public class PolyArray {
          public static void main(String[] args) {
              Person[] persons = new Person[5];
              persons[0] = new Person("jack",20);
              persons[1] = new Student("mary",18,100);
              persons[2] = new Student("smith",19,30.1);
              persons[3] = new Teacher("scott",30,20000);
              persons[4] = new Teacher("king",50,25000);
              for (int i = 0; i < persons.length; i++) {
                  //person[i]编译类型是Person,运行类型是根据实际情况由jvm判断
                  System.out.println(persons[i].say());//动态绑定机制
                  if (persons[i] instanceof Student) {//判断persons[i]的运行类型是否为Student
                      ((Student) persons[i]).study();//向下转型
                  } else if (persons[i] instanceof Teacher) {
                      ((Teacher)persons[i]).teach();//向下转型
                  }
              }
      
          }
      /*输出结果:
      jack	20
      mary	18	score=100.0
      学生 mary正在学习...
      smith	19	score=30.1
      学生 smith正在学习...
      scott	30	salary=20000.0
      老师 scott正在授课...
      king	50	salary=25000.0
      老师 king正在授课...
      */
      
    • public class Person {
          private String name;
          private int age;
      
          public Person(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public String say() {
              return name + "\t" + age;
          }
      }
      
    • public class Student extends Person {
          private double score;
      
          public Student(String name, int age, double score) {
              super(name, age);
              this.score = score;
          }
      
          public double getScore() {
              return score;
          }
      
          public void setScore(double score) {
              this.score = score;
          }
      
          @Override
          public String say() {
              return super.say() + "\tscore=" + score;
          }
          public void study() {
              System.out.println("学生 " + getName() + "正在学习...");
          }
      }
      
    • public class Teacher extends Person {
          private double salary;
      
          public Teacher(String name, int age, double salary) {
              super(name, age);
              this.salary = salary;
          }
      
          public double getSalary() {
              return salary;
          }
      
          public void setSalary(double salary) {
              this.salary = salary;
          }
      
          @Override
          public String say() {
              return super.say() + "\tsalary=" + salary;
          }
          public void teach() {
              System.out.println("老师 " + getName() + "正在授课...");
          }
      }
      
7.7.6.2 多态参数
  • 介绍

    • 方法定义的形参类型为父类类型,实参类型为子类类型
  • 案例

    1. 主人喂动物
    2. 定义员工类Employee,包含姓名和月工资【private】,以及计算年工资的方法getAnnual(),普通员工和经理继承员工,经理多了奖金bonus属性和管理方法manage(),普通员工多了方法work(),普通员工和经理要求分别重写getAnnual()方法,测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法;测试类中添加一个方法testWork(),如果是普通员工则调用work()方法,如果是经理则调用manage()方法
    • 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();
              System.out.println(tom.getName() + "的年薪为" + polyParameter.showEmpAnnual(tom));
              System.out.println(milan.getName() + "的年薪为" + polyParameter.showEmpAnnual(milan));
              polyParameter.testWork(tom);
              polyParameter.testWork(milan);
          }
          public double showEmpAnnual(Employee e) {
              return e.getAnnual();
          }
          public void testWork(Employee e) {
              if (e instanceof Worker) {
                  ((Worker)e).work();
              } else if (e instanceof Manager) {
                  ((Manager)e).manage();
              } else {
                  System.out.println("不做处理...");
              }
          }
      }
      /*
      tom的年薪为30000.0
      milan的年薪为260000.0
      普通员工tom正在工作...
      经理milan正在管理...
      */
      
    • public class Employee {
          private String name;
          private double salary;
      
          public Employee(String name, double salary) {
              this.name = name;
              this.salary = salary;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public double getSalary() {
              return salary;
          }
      
          public void setSalary(double salary) {
              this.salary = salary;
          }
      
          public double getAnnual() {
              return 12 * salary;
          }
      }
      
    • public class Manager extends Employee {
          private double bonus;
      
          public Manager(String name, double salary, double bonus) {
              super(name, salary);
              this.bonus = bonus;
          }
      
          public double getBonus() {
              return bonus;
          }
      
          public void setBonus(double bonus) {
              this.bonus = bonus;
          }
          public void manage() {
              System.out.println("经理" + getName() + "正在管理...");
          }
      
          @Override
          public double getAnnual() {//经理多了奖金
              return super.getAnnual() + bonus;
          }
      }
      
    • public class Worker extends Employee {
          public Worker(String name, double salary) {
              super(name, salary);
          }
          public void work() {
              System.out.println("普通员工" + getName() + "正在工作...");
          }
      
          @Override
          public double getAnnual() {//普通员工没有其他收入,直接调用即可
              return super.getAnnual();
          }
      }
      

7.8 Object类详解

7.8.1 equals方法

7.8.1.1 equals方法

==与equals的对比:

  1. ==:既可以判断基本类型,又可以判断引用类型
    1. 如果判断基本类型,判断的是值是否相等。例如:int i = 10;double d = 10.0;
    2. 如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
  2. equals:是Object类中的方法,只能判断引用类型
    1. 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。例如Integer和String,看源码
7.8.1.2 重写equals方法
  1. 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。

    1. public class EqualsExer01 {
          public static void main(String[] args) {
              Person person1 = new Person("jack", 10, '男');
              Person person2 = new Person("jack", 10, '男');
              Person person3 = new Person("smith", 10, '男');
              System.out.println(person1.equals(person2));    //true
              System.out.println(person1.equals(person3));    //false
          }
      }
      class Person {
          private String name;
          private int age;
          private char gender;
      
          public boolean equals(Object obj) {
              if (this == obj) {
                  return true;
              }
              if (obj instanceof Person) {
                  Person obj1 = (Person) obj;
                  return this.name.equals(obj1.name) && this.age == obj1.age && this.gender == obj1.gender;
              }
              return false;
          }
          public Person(String name, int age, char gender) {
              this.name = name;
              this.age = age;
              this.gender = gender;
          }
          public String getName() {
              return name;
          }
          public void setName(String name) {
              this.name = name;
          }
          public int getAge() {
              return age;
          }
          public void setAge(int age) {
              this.age = age;
          }
          public char getGender() {
              return gender;
          }
          public void setGender(char gender) {
              this.gender = gender;
          }
      }
      
7.8.1.3 equals练习题
  1. public class EqualsExer02 {
        public static void main(String[] args) {
            Person_ p1 = new Person_();
            p1.name = "hspedu";
            Person_ p2 = new Person_();
            p2.name = "hspedu";
            System.out.println(p1==p2); //False
            System.out.println(p1.name.equals( p2.name));//T
            System.out.println(p1.equals(p2));//False
            String s1 = new String("asdf");
            String s2 = new String("asdf");
            System.out.println(s1.equals(s2));//T
            System.out.println(s1==s2); //F
        }
    }
    class Person_{//类
        public String name;
    }
    
  2. public class EqualsExer03 {
        public static void main(String[] args) {
            int it = 65;
            float fl = 65.0f;
            System.out.println("65 和 65.0f 是否相等?" + (it == fl));//T
            char ch1 = 'A'; char ch2 = 12;
            System.out.println("65 和'A'是否相等?" + (it == ch1));//T
            System.out.println("12 和 ch2 是否相等?" + (12 == ch2));//T
            String str1 = new String("hello");
            String str2 = new String("hello");
            System.out.println("str1 和 str2 是否相等?"+ (str1 == str2)); //F
            System.out.println("str1 是否 equals str2?"+(str1.equals(str2)));//T
            System.out.println("hello" == new java.sql.Date());//编译错误
        }
    }
    

7.8.2 hashCode方法

7.8.2.1 总结
  1. 提高具有哈希结构的容器的效率
  2. 两个引用,如果指向的是同一个对象,则哈希值一样
  3. 两个引用,如果指向的是不同的对象,则哈希值不一样
  4. 哈希值是根据地址号转换得到的,不能完全将哈希值等价于地址(通常通过将对象的内部地址转换为整数来实现)
  5. 在集合章节介绍如何重写hashCode方法
7.8.2.2 案例
public class HashCode_ {
    public static void main(String[] args) {
        AA aa = new AA();
        AA aa2 = new AA();
        AA aa3 = aa;
        System.out.println("aa.hashCode()=" + aa.hashCode());//aa.hashCode()=460141958
        System.out.println("aa2.hashCode()=" + aa2.hashCode());//aa2.hashCode()=1163157884
        System.out.println("aa3.hashCode()=" + aa3.hashCode());//aa3.hashCode()=460141958
    }
}
class AA {}

7.8.3 toString方法

7.8.3.1 介绍
  • 默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】

  • 子类往往重写 toString 方法,用于返回对象的属性信息

  • 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式

  • 当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用 monster.toString();

7.8.3.2 重写toString方法
public class ToString_ {
    public static void main(String[] args) {
        /*
        Object 的 toString() 源码
        (1)getClass().getName() 类的全类名(包名+类名 )
        (2)Integer.toHexString(hashCode()) 将对象的 hashCode 值转成 16 进制字符串
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
        */
        Monster monster = new Monster("小妖怪", "巡山", 1000);
        System.out.println(monster.toString());
        //重写toString方法前,输出tostring.Monster@1b6d3586
        //1b6d3586就是对象的hashCode值转换成16进制得到的
        //重写toString方法后,Monster{name='小妖怪', job='巡山', salary=1000.0}
    }
}
class Monster {
    private String name;
    private String job;
    private double salary;

    public Monster(String name, String job, double salary) {
        this.name = name;
        this.job = job;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", salary=" + salary +
                '}';
    }
}

7.8.4 finalize方法

  • 当对象被回收时,系统会自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
  • 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
  • 垃圾回收机制的调用,是由系统来决定的(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制
public class Finalize_ {
    public static void main(String[] args) {
        Car bmw = new Car("宝马");
        //这时 car 对象就是一个垃圾,垃圾回收器就会回收(销毁)对象, 在销毁对象前,会调用该对象的 finalize 方法
        //程序员就可以在 finalize 中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开的文件..)
        //如果程序员不重写 finalize,那么就会调用 Object 类的 finalize, 即默认处理
        //如果程序员重写了 finalize, 就可以实现自己的逻辑
        bmw = 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("释放了某些资源...");
    }
}
/*输出:
程序退出了....
我们销毁 汽车宝马
释放了某些资源...
*/

7.9 断点调试(debug)

7.9.1 介绍

  1. 在断点调试过程中,是运行时状态,是以对象的运行类型来执行的
  2. 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找出这个bug
  3. 断点调试是程序员必须掌握的技能
  4. 断点调试也能帮我们查看Java底层源代码的执行过程,提高程序员的Java水平

7.9.2 快捷键

F5:步入(跳入方法内)

F6:步过(逐行执行代码)

F7:跳出(跳出方法)

F8:恢复程序resume,执行到下一个断点

在这里插入图片描述

7.9.3 应用案例

断点调试查看对象创建过程

public class Test {
    public static void main(String[] args) {
        //创建对象的流程
        //1. 加载Person类信息
        //2. 初始化 2.1 默认初始化,2.2 显式初始化,2.3 构造器初始化
        //3. 返回对象的地址
        Person jack = new Person("jack", 20);
        System.out.println(jack);
    }
}
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

断点调试查看动态绑定工作机制

public class Test {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());    //B类的sum()注释前输出40,注释后输出30
        System.out.println(a.sum1());   //B类的sum1()注释前输出30,注释后输出20
    }
}

class A {
    public int i = 10;
    //当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
    //a的运行类型是B类,调用B类的getI()方法
    public int sum() {
        return getI() + 10;
    }
    //当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
    //此处的i是A类编译时声明的,所以值为10
    public int sum1() {
        return i + 10;
    }
    public int getI() {
        return i;
    }
}
class B extends A {
    public int i = 20;
    /*public int sum() {
        return i + 20;
    }*/
    public  int getI() {
        return i;
    }
    /*public int sum1() {
        return i + 10;
    }*/
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值