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 注意事项
- 修饰符可以用来修饰类中的属性、成员方法、类
- 只有public和默认才能修饰类,并遵守上述访问权限的特点
- 子类中的访问权限放在继承章节
- 成员方法的访问规则和属性一样
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的比较
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 从父类开始查找属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 从父类开始查找方法 |
调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
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 对象的多态
重要说明:
-
一个对象的编译类型和运行类型可以不一致
Animal animal = new Dog();
- 父类的一个引用指向子类的一个对象
- animal编译类型是Animal,运行类型是Dog
-
编译类型在定义对象时,就确定了,不能改变
-
运行类型是可以变化的
animal = new Cat();
- animal的运行类型变成了Cat,编译类型仍是Animal
-
编译类型看定义时 = 号的左边,运行类型看 = 号的右边
举例
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的动态绑定机制 !!!
- 当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
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 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组 中,并调用每个对象 say 方法
-
- 应用实例升级:如何调用子类特有的方法,比如 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 多态参数
-
介绍
- 方法定义的形参类型为父类类型,实参类型为子类类型
-
案例
- 主人喂动物
- 定义员工类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的对比:
- ==:既可以判断基本类型,又可以判断引用类型
- 如果判断基本类型,判断的是值是否相等。例如:
int i = 10;double d = 10.0;
- 如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
- 如果判断基本类型,判断的是值是否相等。例如:
- equals:是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。例如Integer和String,看源码
7.8.1.2 重写equals方法
-
判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。
-
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练习题
-
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; }
-
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 总结
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值一样
- 两个引用,如果指向的是不同的对象,则哈希值不一样
- 哈希值是根据地址号转换得到的,不能完全将哈希值等价于地址(通常通过将对象的内部地址转换为整数来实现)
- 在集合章节介绍如何重写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 介绍
- 在断点调试过程中,是运行时状态,是以对象的运行类型来执行的
- 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找出这个bug
- 断点调试是程序员必须掌握的技能
- 断点调试也能帮我们查看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;
}*/
}