这次是2021年01月18日的学习成果,总结于此,以便于之后的学习查漏补缺
继承
继承是面向对象程序的一个基本特征,通过继承可以实现父子关系,以及代码的复用
继承的作用
1、描述类和类之间的关系
2、降低类和类之间的重复代码
extends 关键字
在类的声明中,通过extends 关键字声明一个继承关系。java只支持单继承,每个java只能有一个父类。
例如,下面的代码中,Father是父类,Son是子类,二者之间有继承关系:
public class Son extends Father{
}
Java类体系中的根类是java.lang.Object,所有类都是Object的直接子类或间接子类。如果定义类时没有extends子句,则该类的父类默认为Object
instanceof
instanceof关键字用来判断一个对象是否是指定类所创建的实例,返回值是boolean
语法:
boolean flag = 对象 instanceof 类
示例:
Person person = new Person();
System.out.println(person instanceof Person);
方法重写(重点)
在继承中,子类可以定义和父类相同的名称且参数列表一致的函数,将这种函数称之为函数的重写。简而言之,重写方法就是在子类中重新定义父类中已有的方法。
我们可以说:
1、重载是一个类中一个方法功能的多种表现形态
2、重写是父子类对于同一个方法表现出不同的形式
1、重写要求
1、在子类中的重写方法和父类中的被重写方法,两个方法名称和参数列表必须相同。
2、子类中重写方法的访问权限必须大于或等于父类中被重写方法的访问权限,否则编译报错。( public > protected > default > private )
3、子类中重写方法的返回值类型必须是父类中方法的返回值类型或该类型的子类,不能返回比父类更大范围的数据类型。
4、子类中重写方法不能比父类中被重写方法产生更多的异常类型。
5、父类的构造函数是不能子类继承的,因此不能在子类中重写。但是,在子类中可以使用super关键字访问父类中的构造函数。
2、重写方法调用顺序
1、子类中重写后的方法在方法体的第一行默认调用父类被重写的方法
//父类的方法
public class Father {
public void buy(int money) {
System.out.println("这是父类的buy方法"+money);
}
}
public class Son extends Father{
//子类中重写后的方法
@Override
public void buy(int money) {
super.buy(money); //默认调用,可删除
System.out.println("这是子类的buy方法"+money);
}
}
调用子类的buy方法,执行结果:
父类的buy方法:10
子类的buy方法:10
2、子类的对象调用重写方法时依据就近原则优先调用子类重写后的方法
3、子类中调用父类的方法
super.方法名()
3、方法重写和重载的主要区别(重点)
重写用于继承关系的父子类中,不能用于同一个类中。
重写用于更改父类中方法的行为,或者是实现接口中的方法。
重写特点:函数名必须相同、参数列表必须相同。子类的返回值类型要等于或者小于父类的返回值等,子类的作用域不能小于父类中方法的作用域,子类抛出的异常类型不能大于父类中方法的异常类型。
重载通常用于同一个类中。重载用于为一个行为提供多种实现方式。
重载特点:函数名相同,参数列表不同,与访问控制符、返回值类型等无关。
构造函数和super(重点)
1、super
super表示对当前对象的父类对象的一个引用,用于访问父类的属性,父类的方法和父类的构造方法。
public class Father {
int x = 1;
public Father() {
System.out.println("这是父类的无参构造");
}
public Father(int x) {
this.x = x;
System.out.println("这是父类的有参构造");
}
public void speak() {
System.out.println("这是父类的speak方法");
}
}
public class Son extends Father{
int y = 1;
public Son() {
System.out.println("这是子类的无参构造");
}
public Son(int y) {
this.y = y + x; //访问父类属性x,也可写作super.x
System.out.println("这是子类的有参构造");
}
public void run() {
super.speak(); //显示访问父类的函数
System.out.println("这是子类的run方法");
}
}
public class Test1 {
public static void main(String[] args) {
Son son = new Son(2);
System.out.println(son.y); //输出结果为3
}
}
this和super小结
this和super很像,this指向当前对象,super指向当前对象的父类
2、构造函数
**创建子类对象时,必须先初始化父类属性,所以必须先执行父类的构造方法再执行子类构造方法。**经典理解:先有父亲,才会有儿子。在Java的继承体系中亦如此。
子类不能继承父类的构造函数,但子类构造函数里必须先调用(隐式调用或显式调用)父类的构造函数。
(1)隐式调用父类构造函数(默认情况)
在子类的构造函数中,如果没有显示地调用父类中的构造函数,那么默认会调用父类中的无参构造函数。如果父类中没有无参构造函数则编译报错。
public class Father {
public Father() {
System.out.println("这是父类的无参构造函数");
}
}
public class Son extends Father{
public Son() {
super(); //默认调用父类的无参构造函数
System.out.println("这是子类的无参构造函数");
}
}
(2)显示调用父类中构造函数
如果在子类中使用super显式调用父类中带参数的构造函数,那么编译器不会调用父类中无参构造函数的代码。请注意:调用父类构造函数的super只能放在子类的构造函数的第一行,且只能调用一-次。
例如,在子类Son的构造函数中使用super()调用父类的构造函数。
public class Father {
int x = 2;
int y = 1;
public Father(int y) {
super();
this.y = y;
}
}
public class Son extends Father{
int y = 2;
public Son(int y) {
super(y); //子类显式调用父类构造函数
this.y = y + x;
System.out.println("子类的有参构造函数");
}
}
注意:在同一构造函数中,不能同时存在super()和this(),因为他们都要求放在构造函数的第一行代码
继承特性总结
1、Java 只支持单继承,不允许多重继承;但是Java支持多层继承
(1)、 一个子类只能有一个父类;只有唯一的亲生父亲
(2)、一个父类可以派生出多个子类;子孙满堂
(3)、Java支持多层继承;代代相传
(4)、被继承的类称之为父类(基类),继承的类称之为子类
2、子类可以继承父类的非私有属性和非私有方法
3、子类可以扩展父类没有的属性和方法
4、子类不会继承父类的构造方法。
5、子类一定会调用父类的构造器。因为在创建子类对象时,一定会先为从父类继承的属性进行初始化,所以要调用父类的构造器
6、在子类中无论写不写super( )都会调用父类的无参构造。如果要写,则必须在子类构造器首行。如果父类没有无参构造,必须在子类的构造器首行使用super(实参列表)显式调用父类的有参构造,否则编译报错;
7、不要为了使用继承而继承。例如:让工人继承学生。
8、当父类引用指向子类对象时:成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边! ! !
final(重点)
final关键字主要用于修饰类、属性、方法,以及方法的形参。final 在类之前,该类不能被继承。final 在方法之前,该方法不可被重写。final 在变量之前,该变量不可被修改,就是一个常量。
1、final修饰类
如果类没有必要进行扩展,为了防止代码功能被重写,通常将类加final 修饰。这样将限制该类不会有继承的子类,如果编写了它的子类,编译时就会报错。
//这是一个final类,它不可被子类继承
public final class FinalArcher {
}
2、final修饰成员属性
常量定义
在变量之前使用final 修饰,就是定义一个常量,表示值不能被修改。
在实际项目中,该关键字一般和static关键字结合使用。这样可以使常量优先加载,不必等到创建对象的时候再初始化,而且访问时不需要事先创建对象实例,直接使用类名就可以访问。
public class MathPI {
//final和static可以互换位置
public static final double PI = 3.14;
}
public class Test {
public static void main(String[] args) {
System.out.println(MathPI.PI);
}
}
3、final修饰方法
如果父类中函数不想被子类继承并重写,可以将该函数使用final修饰
public final double add() {
return 3.0;
}
4、final修饰形参(输入参数)
当形参被修饰为final,那么该形参在所属方法中不能被修改
public static void print(final String[] arr) {
//arr = null;无法重新赋值
}
可见性
在Java语言中,常见的可见性有四种: public、private、 protected、 default (默认,即没有定义) ;即访问控制符。
类的成员的可见性
对类的成员变量和成员方法,我们可通过设定一定的访问可见性来限定应用范围。
小结
当使用访问控制符修饰类的成员(包括成员变量、方法和构造器等)时
1、成员使用private修饰只在本类中使用。
2、public修饰的成员可以被所有类访问
3、若一个成员没有使用任何修饰符,默认为default;该成员可以被包中的其他类访问。
4、被protected修饰的成员可以被包中其他类访问,并且位于不同包中的子类也可访问。
外部类的可见性
对于外部类(即目前学习的普通类)而言,它也可以使用访问控制符修饰,但外部类只能有两种访问控制级别: public 和默认。成员内部类可以使用private。
public类可以被所有类访问。即,使用public 修饰的外部类可以被所有类使用,如声明变量、创建实例。
不使用任何访问控制符修饰的外部类即被默认修饰性修饰的类,只能被同一个包中的其他类使用。
外部类不能使用private 和protected 修饰,因为外部类没有处于任何类的内部,也就没有其所在类的内部、所在类的子类两个范围,因此private和protected访问控制符对外部类没有意义。
抽象类(了解)
抽象类不能被直接实例化。因此它-.般作为其它类的父类,与final类正好相反。
1、语法
(1)抽象类
用abstract 关键字来修饰一个类时, 该类叫做抽象类。
//抽象类
public abstract class sheep {
}
(2)抽象方法
用abstract 来修饰-一个 方法时,该方法叫做抽象方法。抽象方法只能在抽象类中定义。它只有方法声明,没有方法体。
public abstract class Sheep {
//抽象方法
public abstract float area();
}
(3)抽象类特点
1、抽象类中不一定有抽象函数。
2、抽象方法只能在抽象类中定义。
3、抽象类不能直接创建实例,只能通过子类创建实例。
4、编译器强制子类实现抽象父类中的抽象方法。如果子类也是抽象类则可以不实现。
接口(重点)
1、接口的引入
在C++中支持多重继承实现,为了避免多重继承的复杂性,Java 只支持单继承。为了实现类似于多重继承的效果,Java提供了接口来拓展类的功能,弥补Java中单继承的缺点。
2、接口语法
(1)接口定义
接口就是抽象方法和常量值定义的集合。
public interface MyInterface extends MySuperInterface{
//抽象属性
//抽象方法
}
注意:
1、关键字interface
2、接口中方法只有声明,没有定义(没有方法体)
3、接口中的所有属性:默认的修饰符是public static final
4、接口中的所有方法:默认的修饰符是public abstract
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
(2)接口实现(重点)
类与类之间有继承关系,那么接口与类之间又是什么关系呢?接口与类之间是实现关系。类实现接口用关键字implements,一个类可实现一个或多个接口;假若实现了多个接口,每个接口之间用逗号隔开。
单接口实现
public abstract class Sheep implements MyInterface{
}
多接口实现
利用接口可实现类似C++中多重继承的效果,即一个类可以实现多个接口,在implements子句中用逗号分隔。
public class Sheep implements A,B{
public void method1() {}
public void method2() {}
}
接口继承(了解)
接口是支持多重继承的,即一个接口可以有多个父接口
例如,定义下列多个借口
interface A{
void menthod1();
}
interface B{
void menthod2();
}
interface C{
void menthod3();
}
在MyInterface中继承了接口A,B,C ,同时定义了method4:
public interface MyInterface extends A,B,C{
void method4();
}
现在,在MyInterface中有四个方法,包括父接口的method1,method2,method3以及自身的method4
(4)总结
1、接口没有构造方法。
2、接口中定义的所有属性默认是public static final的,即静态常量。
3、接口中定义的所有方法默认是public abstract的,即抽象方法。
4、由于接口中的定义的所有方法默认都是抽象的,所以接口不能被实例化。
5、有抽象函数的不一定是抽象类,也可以是接口。
6、类可以通过implements实现接口,一个类可以实现多个接口。
7、如果类没有实现接口中的所有方法,那么类必须是抽象类。
8、如果在类中访问接口中的属性,不能使用super 关键字,因为两者之间没有显示的继承关系。可以使用 “接口名.属性名” 直接访问。
3、接口与抽象类(了解)
实现接口的类也可以是抽象类,这个类可以不实现接口中的方法,它把这些方法都作为自身的抽象方法。因此,在抽象类中可以不需要实现所有方法,可以留在子类中定义。
public abstract class Sheep implements MyInterface{
}
4、接口应用(重点)
例如, PencilWithEraser 继承了Pencil 的 write 方法具备了写功能,现在希望具备擦功能,由于Java只支持单继承,无法继承Eraser,因此无法具备擦功能。
现在,将 Eraser 定义为接口,从而扩展 PencilWithEraser 的功能,使其具备擦功能。具体实现如下:
铅笔 类
public class Pencil {
public String name;
public Pencil() {
}
public Pencil(String name) {
this.name = name;
}
public void write() {
System.out.println("铅笔写字");
}
}
接口,橡皮
public interface Eraser {
public static final String color = "白色";
public void clean();
}
带橡皮的铅笔 类继承铅笔类,实现橡皮接口
public class PencilWithEraser extends Pencil implements Eraser{
public String name;
public String type;
public PencilWithEraser() {
}
public PencilWithEraser(String name) {
super(name);
}
public PencilWithEraser(String name, String type) {
super();
this.name = name;
this.type = type;
}
@Override
public void write() {
super.write();
System.out.println(name+"考试专用");
}
@Override
public void clean() {
System.out.println("带橡皮的铅笔,就是好用");
}
}
测试类
public class Test {
public static void mian() {
PencilWithEraser pencil = new PencilWithEraser("中华", "2B");
pencil.write();
pencil.clean();
System.out.println(pencil.color);
System.out.println(PencilWithEraser.color);
}
}