- 包
- 静态导入(不推荐)
- 常见系统包
- 封装
- 继承
- 继承的规则
- protected
- this
- super
- this和super的区别
- 继承关系练习题
- final
- 类之间的关系
- 多态
- 向上转型
- 方法重载overload
- 方法重写override
- 向上转型发生的时机
- 向下转型
- instanceof
- 抽象类
- 注意事项
- 接口
- 注意事项
本文承接这篇文章→JavaSE_04_类和对象继续学习面向对象特性 包
-
不写权限访问修饰符,就是包访问权限,在当前包内部可见,只在同级目录下可见,不包含子包。
-
包就是文件夹,项目中的包在本地操作系统中就是文件夹;
-
要创建多级目录,不同包之间用.操作符分隔;
-
类名的全名称是包名.类名,包存在的意义是解决类的重名问题;
-
import语句只能导入某个包中具体某一个类;
-
若要将整个包下的类按需加载,例如import java.util.*,则不需要一个一个导入java.util包中的类,但是一般不推荐这种写法,因为可能会造成歧义:
-
import java.util.*; import java.sql.*; //上述两个包中都有Date类,在使用Date类时,编译器不知道Date类来自于哪个包
-
解决办法:
1.使用类的全名称:java.tuil.Date或java.sql.Date
2.在导入时明确指定Date来自于哪个包
//导入一个类下的所有静态方法和属性 import static java.lang.System.*; //在类中就可以使用out.println()常见系统包
java.lang:JDK的基础类(System,String,Object), JDK1.1之后这个包下的所有类自动导入 java.lang.reflect:反射开发包 java.util:工具包(集合类都在这个包下,Arrays,LinkedList,HashMap) java.io:I/O开发包,文件的读取和写入 java.net:网络开发用到的包,Socket java.sql:数据库开发用到的包
封装
保护性、易用性
方法重载:在同一个类中,方法名称相同,参数列表不同,与返回值类型无关的方法。
继承
减少代码冗余
代码能跑起来是第一步,还要进行CodeReview,避免出现重复代码。在设计模式中进行代码优化。
当一个类继承了另一个类,另一个类中所有属性和方法,子类就天然具备了。
public class Animal{
String name;
public void eat(){
//TODO
}
}
public class Dog extends Animal{
//Animal中的name和eat()在Dog中都有
}
继承的规则
-
继承的父子类之间必须是一个包含另一个的关系
-
Java中只有单继承,一个子类只能使用extends继承一个父类,可以多层继承。
class Animal{} class Dog extends Animal{} class Labuladuo extends Dog{} //继承树在C++中将有继承关系的两个类更多地称为:基类和派生类;而在Java中,更多是叫做父类和子类。
一个爸爸可以有多个儿子,一个儿子只能有一个爸爸。
-
子类会继承父类的所有属性和方法
-
显示继承:父类中public修饰的属性和方法可以在子类中直接使用;
-
隐式继承:父类中private修饰的属性和方法,子类也继承了这些属性和方法,但是无法直接使用。
class Father{ private int password; public int getter(){ return password; } } class Child extends Father{ //子类对象能通过调用getter()访问password }
不同包具有继承关系的类,父类的属性和方法在子类的内部是可见的,出了子类,就不可见了。
//protected权限的属性在子类中的用法 04_28
//protected权限 > (defaulted)包访问权限,对于包权限可见的范围,protected权限一定包含。 //protected的可见范围 : 同一个包(指的是同一个级别的包:同级目录,不包括子包)下没有关系的类之间、不同包有继承关系的类之间this
要产生一个子类对象,默认先产生父类对象。
存在继承时,先从本类找,找不到再向上搜寻。
阿里笔试题(点击查看)
super要进入主方法,先加载主类。这里主类是D,而D又继承自B,所以先加载B类,才能加载成功D类(因为D中有来自B的全部属性和方法)。类一经加载,就执行了静态块。所以先执行了3、6,类加载成功后进入主方法
执行7,接着构造了两次D的对象,子类产生对象时,先要产生一个父类对象。构造对象时先执行构造块,再执行构造方法。每产生一个对象,都要执行进行先构造块、再构造方法的流程。于是,先执行2、1产生父类对象,再执行5、4产生子类对象;下一个D对象的产生同理,再来一次2、1、5、4。最后执行8,主方法结束。
于是,该方法输出顺序即为:3、6、7、2、1、5、4、2、1、5、4、8
-
修饰属性,表示直接从父类中寻找同名属性。
-
存在继承关系时,若要访问成员变量,建议加上this,使用this.属性。不加this的话,遵循就近原则,先从本类对象的属性找。若一开始就想使用父类的该属性,使用super.属性
-
super:先从直接父类(爸爸)寻找同名属性,若不存在,向上搜索(爷爷->太爷爷……)
-
存在继承时,使用this.属性,先从当前类中寻找该属性,若找不到,向父类对象中找。
-
**从直接父类开始向上搜索时,只要找到就停止向上搜索;能不能用该属性取决于该属性在其类内部的访问权限。**比如,在向上找的时候找到了该属性,但是被private修饰,只能在当前父类中使用,这时候也应当停止向上搜索。
-
-
修饰方法->构造方法
-
存在多层继承时,产生子类对象时,先产生父类对象;父类还有父类,继续向上产生。
-
super(父类构造方法的参数)
若父类中不存在无参构造,则子类构造方法的首行必须显式地调用super(父类有参构造的参数)。
class Base{ public Base(String s){ System.out.print("B"); } } public class Derived extends Base{ public Derived (String s) { System.out.print("D"); } public static void main(String[] args){ new Derived("C"); } } //编译错误 -
在一个构造方法中,无法显式地调用this()和super()(两者都必须放在第一行,发生矛盾。)
-
-
修饰成员方法
super不能指代当前父类的对象引用
可以直接打印this,但是不能直接打印super,可以打印super.父类中的属性和方法
继承关系练习题new一个子类对象时,先调用父类构造方法产生一个父类对象,再调用子类构造方法产生一个子类对象。
class X{
Y y=new Y();
public X(){
System.out.print("X");
}
}
class Y{
public Y(){
System.out.print("Y");
}
}
public class Z extends X{
Y y=new Y();
public Z(){
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
//执行结果为:YXYZ
final
- 修饰属性,表示常量:值定义后无法被修改
- 修饰类,无法被继承(比如String类)
- 修饰方法,没有方法复写
除了继承关系还有组合关系
class School{
Student student;
Teacher teacher;
}
//学校里面有学生、老师……
多态
向上转型一个引用可以表现出多种行为(或特性),这就是多态性。
- 向上转型–>父类引用指向子类对象,不一定是直接子类,也可以是孙子类……
- 向上转型发生在有继承关系的类之间
- 向上转型的意义是:参数统一化(使用同一个父类引用接受不同的子类实例)
发生在同一个类中,定义了若干个方法名称相同,参数列表不同,与返回值类型无关
方法重写override发生在有继承关系的类之间,子类定义了和父类除了权限不同,其他都相同的方法
class Animal{
public void eat(){
//1
}
}
class Bird extends Animal{
public void eat(){
//2
}
}
class Duck extends Birds{
public void eat(){
//3
}
public void play(){
//4
}
}
Animal a1 = new Animal();
Animal a2 = new Birds();
Animal a3 = new Duck();
a1.eat();//1
a2.eat();//2
a3.eat();//3
//若Duck类中没有重写eat()方法,则从其直接父类向上搜寻,找到第一个重写eat方法的父类,执行该父类重写的eat方法。
//例如,如果Birds和Duck类都没有重写eat(),a3.eat()执行的是1处;如果Birds类重写了eat(),Duck类没有重写eat(),a3.eat()执行的是2处。
当发生重写时,子类方法权限必须>=父类方法权限才可以重写。但是当父类方法权限是private时,即使子类对应的重写方法的权限大于等于private,也不能发生重写。
权限范围大小比较:public > protected > default(包权限) >private
方法重写的返回值类型必须严格相同,除了向上转型的情况:
class Person{ Person func(){ //1 } } class Student extends Person{ Student func(){ //重写方法1 } } //子类方法返回学生,父类方法返回人->可以,因为学生类继承了人类。 //子类返回人,父类返回学生->不可以
在重写方法前加@Override校验是否符合重写规则
向上转型发生的时机
-
方法传参
-
父类引用指向子类对象
-
方法返回值
返回值类型为父类,返回一个子类对象的引用
Animal animal = new Duck(); animal.play(); //play方法不能调用 //父类引用使用"."操作符调用方法时,能不能调用成功该方法取决于父类中是否包含该方法;调用以后这个方法表现为什么样的状态,取决于new出来的对象是什么类型。
若想调用成功,需借助向下转型
子类名称 子类引用 = (子类名称) 父类引用 Duck duck = (Duck)animal; duck.play() //这就可以访问了
要发生向下转型,要转的引用首先必须是向上转型得来的!!!(否则就会报出ClassCastException)
instanceof在向下转型前,先用if(父类引用 instanceof 子类名称)判断是否可以成功转型。
Animal animal1 = new Animal(); Animal animal2 = new Duck(); animal1 instanceof Duck //返回false animal2 instanceof Duck //返回true抽象类
若需要强制要求子类复写方法,用到抽象类。
注意事项抽象类是普通类的超集,只是比普通类多了一些抽象方法。
-
抽象方法所在的类必须是抽象类;
-
抽象类可以有多个抽象方法,也可以没有抽象方法;
-
子类(非抽象类)继承了抽象类,必须复写父类中所有抽象方法(包括父类继承自父类的父类中的抽象方法,但是父类并未实现该方法);
-
子类(抽象类)继承了抽象类,可以实现父类中的抽象方法,也可以不实现;
-
Java中定义抽象类或者抽象方法使用abstract关键字;
-
只有方法声明,没有方法体(延迟到子类实现),加abstract关键字
public abstract void fun(); //抽象方法的声明
-
Java中,抽象方法不等于没有方法体的方法:
//本地方法 native public final native void notifyAll(); //由JVM实现,JVM是C++写的,这里是调用C++中的方法
-
若一个类声明为抽象类,无法通过该类实例化对象(哪怕该抽象类中一个抽象方法都没有),只能借助子类向上转型变为抽象父类的引用。
//Person类是一个抽象类,Student类继承自Person类 Person person = new Person(); //错误 Person person = new Student(); //正确
-
抽象类虽然没法直接实例化对象,但是也可以存在构造方法。
abstract class B{ B(){ this.print(); } abstract void print(); } public class D extends B{ //private static int num = 10; private int num = 10; @Override void print() { System.out.println("num = " + num); } public static void main(String[] args) { new D(); } } //输出结果为num = 0,若将num设置为静态属性,则输出:num = 10.先执行抽象父类的构造方法,因为此时的this是在类D中且D已经重写了print(),所以执行的是D中的print()。因为此时D对象还未构造完成,num还未成功赋值,所以num的值为默认值0;但如果num是static的属性,那么进入主方法前就已经在类加载的时候给num赋值了,所以num是10.
-
若一个需求既可以使用抽象类也可以使用接口,优先使用接口,抽象类仍然受单继承局限。
为什么要有接口?
为了让一个方法需要有多种类的对象传参时,参数能兼容
接口的两种场景
- 接口表示具备某种行为/能力–>
- 接口表示一种规范/标准–>电脑的USB接口和不同的USB设备
接口中只有全局常量(必须赋初值)和抽象方法(只有极少部分接口含有全局常量),使用Interface声明接口,子类使用implements实现接口。
编码规范:在接口中,方法不用加public abstract, 全局常量不用加 static final.
注意事项设计模式的一个核心思想:开闭原则
对扩展开放,对修改关闭
- 类实现接口,必须复写接口中所有的抽象方法
- 接口允许多实现,一个类可能具备多种能力,多个接口之间用逗号分隔。



