学习笔记之《Java核心技术卷I》---- 第五章 继承

  • Java中所有继承都是公有继承
  • this是当前对象的引用,而super不是一个对象的引用,不能将super赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字
/*
以下代码可以成功编译运行
*/
class Employee{
	private String name;
	public Employee(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
class Manager extends Employee{
	public Manager(String name) {
		super(name);
	}
	public Employee getManager() {
		return this;
	}
}


/*
以下代码编译出错
*/
class Employee{
	private String name;
	public Employee(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
class Manager extends Employee{
	public Manager(String name) {
		super(name);
	}
	public Employee getManager() {
		return super;
	}
}
  • 如果子类的构造器没有显示地调用超类的构造器,则将自动调用超类默认(没有参数)的构造器
/*
以下代码报错,因为父类Employee没有无参构造器
*/
class Employee{
	private String name;
	public Employee(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
class Manager extends Employee{
	public Manager(String name) {
//		super(name);
	}
	public Employee getManager() {
		return this;
	}
}
  •  关键字this的两个用途:一是引用隐式参数;二是调用该类的其他构造器(此时调用构造器的语句只能作为另一个构造器中第一条语句出现)
  • 关键字super的两个用途:一是调用父类的构造器;二是调用父类的方法
  • 根据引用对象变量实际引用的对象类型,正确调用相应的方法
  • 一个对象变量可以指示多种实际类型的现象称为多态;在运行时能够自动地选择调用哪个方法的现象称为动态绑定
  • 在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链
  • 在进行动态绑定时,如果一个引用变量的声明类型(父类型)与它所指向对象的实际类型(子类型)不同,那么这个引用变量只能调用子类型中那些与父类公有的方法,无论是直接继承而来还是覆盖重写(在此对书本P156第4)点提出质疑),如果调用的是子类新定义的方法,那么程序将会报错。佐证代码如下:
/*
父类Employee中并没有setBonus方法,而staff的声明类型是Employeee,因此在编译staff.setBonus()时出错
*/
public class Test {
	public static void main(String[] args){
		Manager m1 = new Manager("sc");
		Employee staff= new Employee("scc");
		staff = m1;
		staff.setBonus(1000);//出错
		System.out.println(staff.getBonus());
	}
}
class Employee{
	private String name;
	private int bonus = 0;
	public Employee(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
//	public void setBonus(int bonus) {
//		this.bonus = 0;
//	}
	public int getBonus() {
		return bonus;
	}
}
class Manager extends Employee{
	private int bonus = 0;
	public Manager(String name) {
		super(name);
	}
	public void setBonus(int bonus) {
		this.bonus = bonus;
	}
	public int getBonus() {
		return bonus;
	}
}
  • 在子类覆盖父类的某个方法时,允许子类将覆盖方法的返回类型定义为原返回类型的子类型(仍为覆盖)
  • 如果是private方法、static方法或者final方法或者构造器,那么无法动态绑定无法调用这些方法,此时应为静态绑定
  • 在覆盖一个方法时,子类方法不能低于超类方法的可见性。特别:如果超类方法时public,子类覆盖该方法时必须声明为public
  • final:阻止类继承;阻止方法被覆盖;阻止实例域被修改
  • 将一个类声明为final,只有其中的方法自动地成为final(类都不能被继承,何谈方法覆盖),而不包括域。代码佐证如下:
/*
以下代码输出结果为:
5
6
表明了虽然A是final类,但是A.a并不是final型
*/
public class Test1 {
	public static void main(String[] args) {
		new A().test();
	}
}
final class A{
	public int a = 5;
	public final int b = a;
	public void test() {
		System.out.println(a);
		a++;
		System.out.println(a);
	}
}
  • 如果一个方法没有被覆盖并且很短,编译器就能够对它进行优化处理,这个过程称为内联。例如,内联调用e.getName()将被替换为访问e.name域
  • a instanceof A:判断a所指向的对象是否是A的实例
  • abstract:
  1. abstract方法不能写方法实现(实现留给子类或者子类将该方法再声明为abstract或者子类本身声明为abstract)
  2. 包含abstract方法的类必须被声明为abstract
  3. 声明为abstract的类中可以包含通用方法(即非abstract,有实现体的方法)
  4. 声明为abstract的类不能创建对象实例(即不能使用new操作
  5. abstract不能与private、static、final、native并列修饰同一个方法。因为若方法为private,则该方法无法被继承,自然也就无法被实现;若方法为static、final或者native(native不太懂),则应该写清楚方法的实现体(不写清方法体怎么让类或者对象调用),与abstract矛盾
  6. 若要调用abstract类中的static方法,直接使用类名.方法名即可;若要调用abstract类中其它非static非abstract的方法,则可使用子类对象,然后强制转成父类对象,再调用abstract类中相应方法即可。佐证代码如下:
/*
输出结果为:
print1
print2
*/
public class Test1 {
	public static void main(String[] args) {
		A a = new B();
		a = (A) a;
		a.print1();
		A.print2();
	}
}
abstract class A{
	public abstract void test();
	public static void print2() {
		System.out.println("print2");
	}
	public void print1() {
		System.out.println("print1");
	}
}
class B extends A{
	@Override
	public void test() {
		// TODO Auto-generated method stub	
	}
}
  • 用于控制可见性的4个访问修饰符:
  1. 仅对本类可见------private
  2. 对所有类可见------public
  3. 对本包和所有子类可见------proteced
  4. 对本包可见------默认,不需要修饰符
  • 在Java中,只有基本类型(八大基本类型)不是对象
  • Object类中equals方法判断的是两个对象是否具有相同的引用。若其中一个对象为null,则会报NullPointerException
  • Objects(注意有个s)类中的静态方法equals(obj1,obj2)可比较obj1和obj2至少有一个null的情况下obj1和obj2是否相等。若obj1和obj2都是null,则返回true;若只有一个null,则返回false;若都不是null,则判断obj1和obj2是否是指向同一个对象
  • equals方法应具有下面的特性:
  1. 自反性。即x.equals(x)应返回true
  2. 对称性。即x.equals(y)应与y.equals(x)返回同样的结果
  3. 传递性。即若x.equlas(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true
  4. 一致性。如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果
  5. 对于任意非空引用x,x.equals(null)应该返回false
  • 对于P169关于编写一个完美的equals方法的建议第4)点,理解如下:如果每个子类对于equals的结果都有一样的比较标准(比如说都是比较名字,薪水,id),那么只需要判断每个子类的父类是不是都是同一个,其中书上的className应该是父类的类名;如果equals的比较标准在子类中有所改变(子类对于父类的equals进行了重写覆盖),那么首先应该判断当前比较的对象是不是同一个类(因为它们的各自的equals方法比较的标准可能都不相同)。佐证代码如下:
/*
子类Manager和Boss并没有重写父类Employee的equals方法
因此当执行m1.equals(b)以及b.equals(m1)时将继续调用父类
的equals方法判断。此时父类equals使用的一个判断条件是判断显示参数
obj是否为Employee的实例,且只是判断了name和salary是否一样
*/
public class Test {
	public static void main(String[] args){
		Manager m1 = new Manager("sc", 100, 1);//id=1
		Boss b = new Boss("sc", 100, 10);//id=10
		System.out.println(m1.equals(b));//true
		System.out.println(b.equals(m1));//true
	}
}
class Employee{
	public String name;
	public int salary;
	public int id;
	public Employee(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		this.name = name;
		this.salary = salary;
		this.id = id;
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(this == obj) return true;
		if(obj == null || !(obj instanceof Employee)) return false;
		Employee other = (Employee) obj;
		return name.equals(other.name) && salary == other.salary;
	}
}
class Manager extends Employee{
	public Manager(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		super(name, salary, id);
	}
}
class Boss extends Employee{
	public Boss(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		super(name, salary, id);
	}
}

如果子类Manager改写了从父类继承来的equals方法,那么子类的equals方法中应该使用判断条件getClass而不是继续使用instanceof。此时,Boss类的equals方法也应该重写(因为得满足equals的对称性)。佐证代码如下:

/*
在子类改写的equals方法中,不仅比较名字,薪水是否一样,还比较了id是否一样
*/
public class Test {
	public static void main(String[] args){
		Manager m1 = new Manager("sc", 100, 1);
		Boss b = new Boss("sc", 100, 10);
		System.out.println(m1.equals(b));//false
		System.out.println(b.equals(m1));//false
	}
}
class Employee{
	public String name;
	public int salary;
	public int id;
	public Employee(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		this.name = name;
		this.salary = salary;
		this.id = id;
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(this == obj) return true;
		if(obj == null || !(obj instanceof Employee)) return false;
		Employee other = (Employee) obj;
		return name.equals(other.name) && salary == other.salary;
	}
}
class Manager extends Employee{
	public Manager(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		super(name, salary, id);
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(this == obj) return true;
		if(obj == null || getClass() != obj.getClass()) return false;
		Manager other = (Manager) obj;
		return name.equals(other.name) && salary == other.salary && id == other.id;
	}
}
class Boss extends Employee{
	public Boss(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		super(name, salary, id);
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(this == obj) return true;
		if(obj == null || getClass() != obj.getClass()) return false;
		Boss other = (Boss) obj;
		return name.equals(other.name) && salary == other.salary && id == other.id;
	}
}
  • hashCode()方法默认返回的是对象的地址
  • 如果重新定义equals方法,那么就必须重新定义hashCode方法。并且equals方法与hashCode方法的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。代码如下:
public class Test {
	public static void main(String[] args){
		Manager m1 = new Manager("sc", 100, 1);
		Boss b = new Boss("sc", 100, 10);
		System.out.println(m1.equals(b));//true
		System.out.println(m1.hashCode() + " " + b.hashCode());//114645 114645
	}
}
class Employee{
	public String name;
	public int salary;
	public int id;
	public Employee(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		this.name = name;
		this.salary = salary;
		this.id = id;
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if(this == obj) return true;
		if(obj == null || !(obj instanceof Employee)) return false;
		Employee other = (Employee) obj;
		return name.equals(other.name) && salary == other.salary;
	}
	@Override
	public int hashCode() {
		// TODO Auto-generated method stub
		return Objects.hash(name,salary);//使用Objects的静态方法hash()返回多个参数的散列码组合
	}
}
class Manager extends Employee{
	public Manager(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		super(name, salary, id);
	}
//	@Override
//	public boolean equals(Object obj) {
//		// TODO Auto-generated method stub
//		if(this == obj) return true;
//		if(obj == null || getClass() != obj.getClass()) return false;
//		Manager other = (Manager) obj;
//		return name.equals(other.name) && salary == other.salary && id == other.id;
//	}
}
class Boss extends Employee{
	public Boss(String name,int salary,int id) {
		// TODO Auto-generated constructor stub
		super(name, salary, id);
	}
//	@Override
//	public boolean equals(Object obj) {
//		// TODO Auto-generated method stub
//		if(this == obj) return true;
//		if(obj == null || getClass() != obj.getClass()) return false;
//		Boss other = (Boss) obj;
//		return name.equals(other.name) && salary == other.salary && id == other.id;
//	}
}
  • Arrays.toString():打印一维数组元素     Arrays.deepToString():打印多维数组元素
  • java.lang.Object中:native Class getClass():返回类信息   java.lang.Class中: Class getSuperclass():返回当前类的超类信息  String getName():返回当前类的具体名称信息(包括包名) String getSimpleName():返回当且类名(不包括包名)
  • List中的set只能修改数组中已经存在的元素!!!
ArrayList<Integer> list = new ArrayList<>();
list.set(0, 1);
System.out.println(list.get(0));//IndexOutOfBoundsException
  • List转成数组并打印数组
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
	list.add(i);
}
Integer[] a = new Integer[list.size()];//不能写int,必须是Integer,泛型不支持基本类型
a = list.toArray(a);//P181书上写的是list.toArray(a)  如果按书上写的,假如a.length() < list.size(),那么操作结束后,a将不会有任何变化,每个元素都是null(因为a每个元素是Integer(int的对象形式???))
System.out.println(Arrays.toString(a));
  • 装箱:int  --> Integer  拆箱:Integer  -->int
  • 对于boolean、byte、char<=127以及介于-128~127之间的short和int,如果两个变量Integer a,Integer b的值在此范围并且相等,那么a==b的结果将是true;否则a==b为false
Integer a = 100;
Integer b = 100;
System.out.println(a == b);//true
Integer c = 128;
Integer d = 128;
System.out.println(c == d);//false
  • 包装器类引用可以为null
Integer a = null;
System.out.println(a);//null
  • 反射(没搞明白,先跳过)

猜你喜欢

转载自blog.csdn.net/smart_ferry/article/details/84650649