Java学习笔记-继承

继承时面向对象的重要特征之一,在面向对象中,通过继承得到父类的属性和方法,是的代码得到复用

概述

  • 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可
  • 多个类可以称为子类,单独这个类称为父类或者超类
  • 子类可以直接访问父类中的非私有的属性和行为
  • 通过extends关键字让类与类之间产生继承关系
class SubDemo extends Demo{}
  • 继承的出现提高了代码的复用性
  • 继承的出现让类与类之间产生了关系,提供了多态的前提

继承的特点

Java只支持单继承,不支持多继承:一个类只能有一个父类,不可以有多个父类

class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error

Java支持多层继承(继承体系)

lass A{}
class B extends A{}
class C extends B{}

定义继承需要注意:

  • 不要仅为了获取其他类中某个功能而去继承
  • 类与类之间要有所属(“is a”)关系,xx1是xx2的一种

类的继承

  • 面向对象三大特征之一,也是实现软件复用的重要手段。Java的继承只允许单继承
  • Java的继承由关键字extends来实现:
修饰符 class SubClass extends SuperClass
{
	//类定义部分
}
  • 子类可以获得父类全部的成员变量和方法,而不能获得父类的构造器。如果没有显示指定一个类的父类,那么这个类默认继承java.lang.Object类,因此所有类都直接或间接继承自java.lang.Object,故可以调用java.lang.Object所定义的实例和方法

  • 在继承过程中,子类获得了父类的成员变量和方法,但有时父类的方法并不适合子类,此时子类可以重写父类的方法,这种现象称之为方法重写(override)。重写遵循 “两同两小一大”:方法名相同,参数列表相同;返回值类型比父类更小或相等,声明抛出的异常比父类更小或相同;访问权限比父类更大或相等。重写的方法必须一致,要么都是类方法,要么都是实例方法。当在子类中重写了父类的方法时,子类将无法访问父类中被覆盖的方法,只能通过super关键字进行调用。如果父类方法具有private访问权限,那么该方法对子类隐藏,子类无法访问该方法,也就无法重写该方法。此时如果定义一个与父类方法表面上相同的重写方法,此时依旧不是方法重写,只是新定义了一个方法。方法重载(overload)和方法重写(override)区别在于前者是指一个类里面的多个方法,后者是指父类与子类之间的方法

  • super用于限定该对象调用它从父类继承得到的实例变量或方法。与this一样,super也不能出现在static修饰的方法中。如果在构造器中使用super,那么super用于限定该构造器初始化的是该对象从父类继承得到的实例变量。这种情况常用于父类成员变量被子类覆盖而用需要使用父类的成员变量的时候

  • 在某个方法中访问名为a的成员变量,但却没有显式指定调用者,那么系统查找a的顺序为:当前方法的局部变量 -> 当前类的成员变量 -> 当前类父类的成员变量 ··· -> java.lang.Object,如果依旧找不到,那么编译将会报错

  • 子类的所有成员变量在实例被创建时会分配内存空间,父类的成员变量也会分配内存空间。且二者不存在子类覆盖父类内存空间的问题

  • 子类不会获得父类的构造器,但子类可以调用父类构造器来初始化代码。此时使用super来完成。此时super必须位于子类构造器的第一行,故super与this不可同时存在于一个构造器中。无论是否使用super调用来执行父类构造器的初始化方法,子类构造器总会调用父类构造器一次,且都会在子类构造器方法体执行之前执行

super关键字

  • super和this的用法相同
  • this代表本类应用
  • super代表父类引用
  • 当子父类出现同名成员时,可以用super进行区分
  • 子类要调用父类构造函数时,可以使用super语句
  • 子类在构造函数调用时,会首先调用父类的构造函数,相当于在子类构造函数中使用了super();
  • 所有的子类构造函数默认第一句都是super();
  • 子类的所有的构造函数,默认都会访问父类中空参数的构造函数

函数覆盖(Override)

  • 子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写
  • 父类中的私有方法不可以被覆盖
  • 在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取
  • 覆盖注意事项:
    • 覆盖时,子类方法权限一定要大于等于父类方法权限
    • 静态只能覆盖静态
  • 覆盖的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容

子类的实例化过程

  • 子类中所有的构造函数默认都会访问父类中空参数的构造函数
  • 因为每一个构造函数的第一行都有一条默认的语句super();
  • 子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的
  • 当父类中没有空参数的构造函数时,子类的构造函数必须通过 this或者super语句指定要访问的构造函数

继承与组合

继承是实现复用的重要手段,但在使用继承的时候,却破坏了封装。组合也是实现复用的重要方式,却能提供更好的封装性

  • 为了保证父类良好的封装性,通常在设计父类时应遵循以下原则:

    • 尽量隐藏父类的内部数据。尽量将父类的成员变量设置为private
    • 不要让子类可以任意访问、修改父类的方法。仅作为父类的辅助方法,可以用private修饰;若希望被外部访问却不希望子类改写,则可以使用final修饰;若只是被子类修改而不希望其他类自由访问,则使用protected修饰
    • 尽量不要在父类构造器中调用将要被子类重写的方法
  • 组合是把旧的对象作为新类的成员变量组合起来,用以实现复用的功能,用户可以看到新类的方法,而无法知晓被组合对象的方法

class Animal{

	private void beat(){
		System.out.println("beat");
	}

	public void breath(){
		beat();
		System.out.println("breath");
	}
}

class Bird{
	//将原来的父类组合到原来的子类,作为子类的一个组合成分
	private Animal a;
	public Bird ( Animal a ){
		this.a = a;
	}
	//重新定义一个自己的breath()方法
	public void breath(){
		//直接服用Animal提供的breath()方法来实现Bird的breath()方法
		a.breath();
	}
	public void fly(){
		System.out.println("fly");
	}
}

public class CompositeTest{

	public static void main ( String [] args ){
		//此时需要显式创建被组合的对象
		Animal al = new Animal();
		Bird b = new Bird(al);
		b.breath();
		b.fly();
	}
}
  • 继承和组合的区别:继承要表达的是一种is-a的关系,而组合表达的是一种has-a的关系

final修饰符

final关键字用于修饰类,变量和方法,表示修饰的类、方法和变量不可改变。final修饰的变量不可被改变,一旦获取了初始值,该final变量的值就不能被重新赋值

  • final可以修饰类,方法,变量

  • final修饰的类不可以被继承

  • final修饰的方法不可以被覆盖

  • final修饰的变量是一个常量。只能被赋值一次

  • 内部类只能访问被final修饰的局部变量

  • final修饰成员变量:一旦有了初始值,就不能被重新赋值。系统默认分配的值为0,’u0000’,false,null。在Java语法中规定:final修饰的成员变量必须由程序员显式地指定初始值。类变量必须在静态初始化块中或者声明变量的时候指定初始化值;实例变量必须在非静态代码块,声明实例变量或构造器中指定初始化值

  • final修饰局部变量:如果final修饰的局部变量在定义时没有指定默认值,则可以在后面的代码中对该final变量赋初始值,但只能一次,不能重复赋值。若定义时候已经赋值,则不可再次赋值

  • final修饰基本数据类型变量和引用类型变量的区别:final修饰基本数据类型时,不能对基本数据类型重新赋值,基本数据类型变量不能被改变。final修饰引用数据类型时,他保存的仅仅是一个引用,保存的地址不变,但地址所在处的内容却可以改变。换句话说final修饰的变量只是直接保存的内容不可改变

  • final如果满足以下三种情况,则相当于一个直接量:

    • 使用final修饰符修饰
    • 在定义该final变量时指定了初始值
    • 该初始值可以在编译时就被确定下来
  • final方法:final修饰的方法不可被重写,一般用于限制子类的重写。Object中的getClass()就是一个final方法。父类的final方法若为public,则子类出现与父类形式上构成重写关系的方法时将出现错误。若要实现子类中类似父类的方法,则在父类中的方法添加private修饰符。此时子类并不是重写父类方法,而是定义了一个新方法。另外,final修饰的方法是可以重载的,仅仅是不能被重写

  • final类:final修饰的类不可以有子类。例如java.lang.Math

  • 不可变类(immutable):创建该类的实例后,该实例的实例变量是不可改变的。Java提供的8个包装类和java.lang.String类都是不可变类。如果创建不可变类,应遵循以下原则:

    • 使用private和final修饰符来修饰该类的成员变量
    • 提供带参数的构造器,用于根据传入的参数来初始化类里的成员变量
    • 仅为该类的成员变量提供getter方法,不要为该类的成员提供setter方法,因为普通方法无法修改final修饰的成员变量
    • 如果有必要,重写Object类的hashCode()和equals()方法。equals()方法更具关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证两个用equals()方法判断为相等的对象的hashCode()也想等
  • 缓存实例的不可变类:不可变类的实例状态不可改变,可以很方便的被多个对象所共享。如果可能,应该将已创建的不可变类的实例进行缓存

处理对象

  • 打印对象和toString方法
Person p = new Person();
···
System.out.println(p);
System.out.println(p.toString);
  • 以上输出的结果完全一样,toString是Object中的一个实例方法,所有的Java对象都具有toString方法。字符串连接符的实质就是调用了toString方法,将其转化成了字符串。toString方法总是返回该对象实现类的 “类名 + @ + hashCode” 也就是自我描述功能。大部分时候需要重写toString方法,打印自己感兴趣的内容

==和equals

==:用于两个基本数据类型的比较,若比较两个引用数据类型,则必须指向同一对象
equals:用于两个引用类型数据的比较,判断引用字符串对象包含的字符序列是否相同

  • "hello"与new String(“hello”)的区别:前者直接使用,JVM使用常量池管理"hello"直接量,而后者先是与前者相同,再生成新的String对象,保存在堆内存中

  • JVM常量池保证相同的字符串直接量只有一个,不会产生多个副本。使用new String()创建出来的字符串对象是运行时创建出来的,保存在运行时内存区(即堆内存)内,不会放入常量池

  • Object提供了equals()方法,不过这个方法与==无区别,比较的是地址,String重写了这个方法

  • 通常重写equals()方法应满足以下条件:

    • 自反性:对任意x,x.equals(x)一定返回true
    • 对称性:对任意x,y,如果y.equals(x)返回true,则x.equals(y)也返回true
    • 传递性:对任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true
    • 一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应保持一致,要么一直是true,要么一直是false
    • 对任何不是null的x,x.equals(null)一定返回false
  • 重写equals()方法示例:

public boolean equals ( Object obj ){
	//如果两个对象为同一个对象
	if ( this == obj )
		return true;
	//当只有obj是ClassName对象	
	if ( obj != null && obj.getClass() == ClassName.class ){
		ClassName className = (ClassName)obj;
		//并且当前对象的某个变量与obj对象的某个变量相等时才判断两个对象相等
		if ( this.getXxx().equals(ClassName.getXxx())){
			retuen true;
		}
	}
	return false;
}
public boolean equals(Object obj){

    if(!(obj instanceof Demo))
        return false;
    Demo d = (Demo)obj;
    ···
}
原文地址:https://www.cnblogs.com/cj5785/p/10664866.html