目录
一、包
1、导入包中的类
2.静态导入
3.包的访问权限控制
二、继承
1.继承概念及使用方法
2.protect
3.小结
4.final
三、多态
1.向上转型
2.动态绑定
2.1重写
2.2重写和重载的区别
3.向下转型
4.super关键字
四、抽象类
1.抽象类介绍
2.抽象类的作用
五、接口
1.语法规则
2.实现多个接口
我们在使用Java编程的时候,面对很多东西并不会像C语言那样去自己实现,例如:我们想要知道数组的长度。在C语言当中我们会使用:
int len = sizeof(arr)/sizeof(arr[0]);
len就是我们想要的数组的长度。但在Java当中我们是不需要再见去求数组长度的,我们可以使用:
int len = arr.length;
但有些时候我们需要一些特殊的函数:
import java.util.Arrays; public class Main { public static void main(String[] args) { int[] arr = {1,2,3,4,5,6}; int[] b = Arrays.copyOf(arr,5); } }
我们就需要在Java文件当中导入类,上述代码当中的Arrays就是一个类,而java.util就是这个类所在的包。
包:1.包是组织类的一种方式 2. 使用包的目的是保证类的唯一性。
使用import static可以导入一些静态的方法和字段,例如:
import static java.lang.System.*; public class Test { public static void main(String[] args) { out.println("hello"); } }
虽然简化了代码,但看起来总是怪怪的代码可读性不强。一般我们不会这样做。
之前我们在类和对象提到过,有些类的成员我们为了保护它会用private进行修饰从而起到保护作用
但是很多时候我们发现如果成员的访问修饰限定符使用public则范围太大了,使用private又太小了。当我们在类的成员前不加任何访问修饰限定符,那么它的范围就是同一个包中的不同类。
我们假如现在要定义一个Dog类和一个Duck类,狗和鸭子都有他们的名字和年龄,都需要吃东西。这些是它们作为动物的共性。我们在定义类的时候,这些共性显然需要在两个类当中重复定义。今天我们只定义两个动物类,如果有一天因为业务需要,要囊括及其多的种类,难道我们还要一个一个地去重复这些代码吗?
在Java当中,为了简化代码,引入了继承这个概念。
语法规则(B继承A):class B extends A {
}
注意事项:
1.子类只能继承一个父类。
2.子类会继承父类所有public成员和方法。
3.private的成员和方法子类无法继承。
4.对于父类的成员和方法,子类对象可以使用super进行引用。
以以下代码为例:
public class Animal { //当某一个类被final修饰的时候,无法被继承 public String name; public int age; protected int score; public Animal(String name, int age) { this.name = name; this.age = age; System.out.println("Animal构造方法"); } public void eat() { System.out.println(this.name + "正在吃"); } } class Dog extends Animal {//一个子类只能继承一个父类 public Dog(String name, int age) { super(name, age);//子类构造时要先帮父类进行构造 System.out.println("Dog的构造方法"); } public void run() { System.out.println(this.name + "正在跑"); } @Override//重写父类方法 public void eat() { System.out.println(this.name + "狼吞虎咽的吃" + super.name); //重写注意点:1.父子类方法名相同,返回值相同(特殊情况:协变类型),参数列表相同 //在父子类的情况下 //被static,final,private修饰的方法不能重写 //子类的访问修饰限定符范围应大于等于父类 } }
我们在定义父类的类的时候可能会定义自己的构造方法,这个时候就要注意,在子类继承父类之后一定要重写构造方法。
代码格式:
public 子类(String name, int age) {
super(name, age);//调用父类构造方法
System.out.println("Dog的构造方法");}
注意:super调用父类构造方法只能放在子类构造方法的第一行,且super关键字不能出现在静态方法中。
刚刚我们说过,如果把父类当中的字段的访问修饰限定符设置为private,那么子类便无法继承父类的该方法和字段,但是有时候因为某些需求,子类需要访问这些成员。那么我们可以尝试使用protected进行修饰成员,既能起到封装的作用也能被子类访问。
protected修饰的成员最大访问范围为:不同包的父子类关系。
Java当中字段一共有四种访问权限:
1.private: 类内部能访问, 类外部不能访问。
2.默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问。 3.protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问。 4.public : 类内部和类的调用者都能访问。以上是这四种访问修饰限定符可访问的范围。
我们在项目当中,类一般最多允许被连续继承三次。那么现在如果我们不希望一个类被继承,我们就可以在类名前加上final进行修饰。
例:
final public class A { protected int a; }
向上转型是在父子类继承的基础上实现的,可以用一句话概括为:父类引用引用子类对象。
例:
public class Animal { public String name; } class Dog extends Animal {//一个子类只能继承一个父类 } public static void main(String[] args) { Animal animal = new Dog("a", 20);//直接赋值向上转型 }
向上转型发生的情况一共有三种:
1.直接赋值(已举例)。
2.方法传参:
public static void main(String[] args) { fun1(new Dog("父类",12)); } public static void fun1(Animal animal) {//传参发生向上转型 animal.eat(); }
3.方法返回:
public static void main(String[] args) { fun2(duck).eat(); } public static Animal fun2(Duck duck) {//返回值发生向上转型 return duck; }
以之前的父子类为例:所有的动物都会吃东西,但是不同动物吃的方式和食物都不相同。那么我们在继承了父类的eat()方法后,如何去体现各个子类当中的不同呢?
首先来介绍一下重写的概念:子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖(Override)。
例:
public class Animal { //当某一个类被final修饰的时候,无法被继承 public String name = "a"; public Animal(String name, int age) { this.name = name; this.age = age; System.out.println("Animal构造方法"); } public void eat() { System.out.println(this.name + "正在吃"); } } class Dog extends Animal {//一个子类只能继承一个父类 public String name = "狗子"; public Dog(String name, int age) { super(name, age);//子类构造时要先帮父类进行构造 //super不能在静态方法内出现 System.out.println("Dog的构造方法"); } @Override//重写父类方法 public void eat() { System.out.println(this.name + "狼吞虎咽的吃" + super.name); } }
运行之后:
我们会发现,当子类重写了父类的某个方法,且父类引用引用子类对象调用该函数时,系统会默认执行子类重写后的函数(调用成员变量也一样,如果子类内有重名的成员,将优先调用子类自身的)。
重写注意事项:
1.被final,private,static修饰的函数不能被重写。
2.重写后,变量名,参数个数和形式都不能改变,返回值也必须保持相同(协变类型除外(父子类))。
3.子类的重写函数权限要大于等于父类。
1.重写范围:父子类继承关系 重载的范围:同一个类当中
2.重写后的参数:保持相同 重载的参数:形参种类和个数至少一个与原函数不一样
3.重写返回值:保持一致(协变除外) 重载返回值:不做要求,可相同可不同,也可以没有
4.方法名:都与原函数一致
5.权限限制:重写:必须大于等于父类函数 重载:没有限制
向下转型:向下转型就是父类对象转成子类对象. 相比于向上转型来说, 向下转型没那么常见,
但是也有一定的用途 。 例:Duck b = (Duck)a;//向下转型,不建议使用。向下转型只能发生在你想要转型的那个子类刚好是父类引用引用的子类
注意事项:向下转型只能在我们想要转型的那个子类刚好是父类引用引用的类型,否则的话就会抛异常。用通俗的话来说:Animal引用引用了Dog类,我们想把它向下转型成Bird,但狗是不会飞的,所以会报错。
所以为了向下转型更安全,我们可以使用instanceof来判定:
Animal animal = new Cat("小猫"); if (animal instanceof Bird) { Bird bird = (Bird)animal; bird.fly(); }
在先前的例子中我们发现,父类当中被重写的方法不会被使用,我们一直在调用的其实是被子类重写后的方法。那么我们可以更进一步的将父类定义为一个抽象类。
例:
abstract public class Animal { protected String name; abstract protected void eat(); protected void drink() { System.out.println("喝水"); } }
注意:1.抽象方法不能是private 2.抽象类不能被实例化 3.抽象类当中可以包含普通的成员和方法,可以被子类直接调用也可以被重写 4.子类继承了一个抽象类,则必须重写父类的抽象方法、
import com.Animal; public class Dog extends Animal{ public void eat() { System.out.println("狗子吃饭"); } }
接口是抽象类的进一步抽象,在抽象类当中除了抽象方法还能定义一些普通的字段和方法。但是在接口当中只能有抽象方法,字段也只能是静态常量。
public interface IShape { void draw(); } public class Cycle implements IShape { @Override public void draw() { System.out.println("○"); } }
注意:
1.使用interface定义一个接口
2.接口中的方法一定是抽象方法,可以省略abstract
3.接口中的方法一定是公开的方法,因此可以省略public
4.Cycle用implements继承接口,意为实现。
5.接口不能被实例化
有时候,一个子类需要继承多个父类。但是子类是只能继承一个父类的,因此我们引入了接口这个概念,一个子类可以继承多个接口。
例:
public class Cycle implements IShape, IColor { @Override public void draw() { System.out.println("○"); } @Override public void color() { System.out.println("red"); } }
使用接口的好处:忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力