Java基础 | 继承,多态,抽象类与接口

发布时间:2024年01月12日

所有知识点均来源于《Java从入门到精通》(第六版)。

类的继承

一类继承另一个类需要用extends关键字

class Child extends Parent{}
  • Java只支持单继承,所以一个类只能有一个父类;
  • 子类在继承父类之后,创建子类对象的同事也会调用父类的构造方法:如果父类和子类都各自有一个无参的构造方法,在main()中创建子类对象的时候,会先执行父类的构造方法,然后再执行子类的构造方法;
  • 子类继承父类之后可以调用父类创建好的属性和方法;
  • Java是可以有多继承关系的,从祖父一直到孙子。
class Telephone{
	String button = "button: 0-9";
	void call(){
		System.out.println("开始拨打电话");
	}
	public Telephone(){
		System.out.println("调用父类构造方法");
	}
}
class Mobile extends Telephone{
	String screen = "screen: 液晶屏";
	public Mobile(){
		System.out.println("调用子类构造方法");
	}
}
public class Demo2{
	public static void main(String[] args){
		Mobile motto = new Mobile();
		System.out.println(motto.button);
		System.out.println(motto.screen);
		motto.call();
	}
}

结果为:

调用父类构造方法
调用子类构造方法
button: 0-9
screen: 液晶屏
开始拨打电话

Object类

Java中所有的类都直接或间接的继承了java.long.object类,object类是java的最高层,用户创建一个类的时候,除非是指定从其他类继承,否则就是从java.long.object类继承而来。
java中的每个类都来自java.long.object,如String类,Integer类都继承与Object类,此外,自定义的类也继承于Object类。由于所有类都是Object的子类,定义的时候会省略extends Object。

class Anything {}
等价于
class Anything extends Object {}

由于所有类都是Object的子类,任何类都可以重写Object类里的方法。
Object类中getClass(), notify(), notifyAll(), wait()等方法都不能被重写,因为这些方法被定义为final型。

getClass()方法

会返回对象执行时的Class实例,然后用实例调用getName()方法可以取得类的名字。

getClass().getname();

toString()方法

会将对象返回为字符串形式,返回一个String实例,是可以被重写的。

public class Student{
	String name;
	int age;
	
	public Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public String toSting () {
		return "我叫"+name+",今年"+age+"岁。";
	}

	public static void main(String[] args) {
		MainDemo s1 = new MainDemo("张三",16);
		System.out.println(s1.toSting());
		MainDemo s2 = new MainDemo("李四",18);
		System.out.println(s2.toSting());
	}
}

结果

我叫张三,今年16岁。
我叫李四,今年18岁。

equals()方法

  • ==比较的是两个对象引用内存地址时候相同
  • equal()比较的是两个对象的实际内容

对象类型的转换

数据类型的转换在上一篇

向上转型

将子类类型的对象转换为父类类型的对象,可以把子类类型的对象直接赋值给父类类型对象,从而实现按照父类描述子类的效果。

class People()
class Teacher extends People {}
public class Demo3{
	public static void main(String[] args){
		People tom = new Teacher();
	}
}

这里的意思是,把教师类new Teacher()的对象赋值给人类People变量tom,进行了向上的转型,是安全的,当然在运用向上转型的过程中,父类的对象是没有办法运用子类独有的属性或者方法的。

向下转型

是不安全的,需要用强制类转换

子类类型 子类对象 = (子类类型)父类对象;
  • 没有继承关系的对象不可以进行向上转型或者向下转型;
  • 父类对象可以强制转型为子类对象,但要先引用这个子类对象。
    也就是说
Bird bird = new Pigeon();
Pigeon pigeon = (Pigeon) bird;

判断对象类型

在向下转型的过程中,如果父类对象不是子类对象的实例,就会出现`ClassCasrExpectation``错误,所以要判断父类对象是否为子类对象的实例。

myobject (某类对象的引用) instanceof ExampleClass (某个类)

这里子类实例 instanceof 父类是可以得到true的。

方法的重载(名字相同但形参不同的方法)

构造方法由类名决定,如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要用类名命名,为了让方法名相同但形参不同的构造方法同时存在,就需要方法的重载。
方法的重载就是在同一个类里允许存在一个以上的同名方法(不仅仅是构造方法),只要这些方法的参数个数或者类型不一样就行。
虽然在方法重载中可以使两个方法的返回类型不同,但是只有返回类型不同并不足以区分两个方法的重载,还需要通过参数的个数以及参数的数据类型来设置。

不定长参数方法

返回值 方法名(参数数据类型...参数名称)

其实这个参数就是个数组int....a其实也就是int[] a

final 关键字

final 变量

在程序运行过程中一直不会改变的量

final int number;
number = 12345;
number = 123; //会报错

final 方法

方法定义为final可以防止子类修改父类的定义与实现方式,同时定义为final的方法执行效率会比非final要高。

private和final

如果一个父类的某个方法被修饰为private,子类将无法访问该方法,自然无法覆盖这个方法,所以被修饰为private的方法被隐式的指定为final类型,所以修饰为private的方法不用再写final。

final不用写
private final void test() {
}

final类

final 类名{}

定义为final的类不能被继承也不允许别人对这个类进行任何改动,这个类里所有的方法都被隐式的设置为final方法,不过final类中的成员变量可以被定义为final或者非final的。
java.long.Mathjava.long.String都是final类的所以
下面两种都是错的。

class MyMath extends java.long.Math {}
class MyString extends java.longString {}

多态

多态是说写一段程序对所有类进行通用的处理,可以以不同的类对象为参数调用用一个方法,可以处理不同对象的问题,不用对所有的子类分别定义作用相同的方法。

class TrafficLights {}

class RedLight extends TrafficLights {}

class YellowLight extends TrafficLights {}

class GreenLight extends TrafficLights {}

public class Demo {
	public static void lighten(TrafficLights light) {
		if(light instanceof RedLight) 
			System.out.println("红灯亮45秒");
		if(light instanceof YellowLight) 
			System.out.println("黄灯亮3秒");
		if(light instanceof GreenLight) 
			System.out.println("绿灯亮30秒");
	}

	public static void main(String[] args) {
		TrafficLights red = new RedLight();
		lighten(red);
		TrafficLights yellow = new YellowLight();
		lighten(yellow);
		TrafficLights green = new GreenLight();
		lighten(green);
	}
}

这里要注意,p不管是Teacher类还是Student类 instanceof People永远都是True的,所以不能再if里写p instanceof People,写了的话就不会走别的condition了。

class People {}
class Teacher extends People {}
class Student extends People {}
public class Demo1 {
	public static void work (People p) {
		if (p instanceof Teacher) {
			System.out.println("教师要好好授课");
		} else if (p instanceof Student) {
			System.out.println("学生要好好学习");
		} else {
			System.out.println("每个人要好好工作");
		}
	}

	public static void main(String[] args) {
		work(new People());
		work(new Teacher());
		work(new Student());
	}
}

抽象类与接口

仅用来描述特征且极具抽象特性的类叫做抽象类,比如图形类,其实并不能给出一个准确的定义(有几条边几个角?都不知道)。

抽象类

一般会讲父称为抽象类,需要用这个父类来进行继承和多态处理。
abstract关键字定义的类叫做抽象类,使用这个关键字定义的方法叫做抽象方法,抽象方法没有方法体,本身也没有什么意义,除非被重写,其实抽象类除了被继承其实也没有意义,而且不能在非抽象类中获取抽象方法,只有抽象类中才有抽象方法

public abstract class Parent{
	abstract void testAbstract();
}

就像上面的例子说的:

  • 红灯黄灯绿灯都是交通灯,所以他们都要继承交通灯,那么交通灯就是父类,红灯黄灯绿灯都是子类;
  • 但是每种灯要等的时间不一样,所以在交通灯类里,亮灯时间的方法没法实现,需要在子类中每次都重写;
  • 我们可以把交通灯类设置为抽象类;
  • 但是交通灯类自己的亮灯时间方法是没什么意义的,只有知道了什么灯才知道多久;
  • 抽象类的子类必须重写抽象类中的抽象方法 ,否则直接报错;
  • 抽象类的子类如果也是抽象类则不用重写抽象方法 。

抽象类的性质

  • 抽象类不能直接实例化;
  • 抽象方法不能是 private 修饰(抽象方法如果没有加访问限定符时,默认是public);
  • 抽象方法不能被 final 和 static 修饰,因为抽象方法要被子类重写;
  • 抽象类必须被继承,并且继承后子类要重写父类中的抽象方法(否则子类也是抽象类,必须要使用 abstract 修饰);
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类;
  • 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。

这里有一个问题是,所有的子类都必须重写抽象方法,那些其实不需要的子类也需要重写。所以如果将方法放在另一个类里,让那些需要重写的类来继承,不需要重写的类继承抽象类,也会产生另一个问题就是我们一开始的前提就是后序所说的所有类都应该继承抽象类,那那些需要重写的子类继承了方法的封装类后就没法继承一开始的抽象类了,为了应对这些问题需要用接口。

接口

可以把接口看成是一种抽象类,接口中的所有方法都没有方法体。
定义接口的方法可以省略publicabstract 关键字。

public interface name{
	void lighten();
}

一个类继承一个父类的同时再实现一个接口,子类与父类之间是 extends 继承关系,类与接口之间是 implements 实现关系,可以写成:

public class Parallelogram extends Quadrangle implements Paintable {

}
  • 接口中,方法必须被定义为public或者abstract形式,其他修饰符不被承认,如果没有声明的话,当public看;
  • 接口是不能用new 实现实例的。
  • 在接口中定义的任何字段都是staticfinal的;
  • Java不允许多重继承,但使用接口就可以实现多重继承,一个类可以同时实现好几个接口:
class 类名 implements 接口1,接口2,接口3
  • 继承一个接口需要实现接口中所有的方法,一个接口也可以继承另一个接口:
interface intf1{}
interface intf2 extends intef1{}

书写建议

  • 在创建接口时,接口的命名一般以大写字母 I 开头
  • 接口的命名一般使用 “形容词” 词性的单词
  • 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性
文章来源:https://blog.csdn.net/zszq111/article/details/135544099
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。