此为个人学习笔记,如有错误,欢迎指教
继承
概述:
多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承这个类即可。这个类称为父类,多个类称为子类。
格式:
class 类名 extends 父类名{}
继承的好处
提高了代码的复用性
提高了代码的维护性
使类与类之间有了关系,多态的前提
继承的弊端:
耦合性很强
#####开发原则:低耦合,高内聚
耦合:类与类的关系
内聚:自己完成事件的能力
子类只能继承父类所有的非死有成员,打破了封装性
继承的特点:
只支持单继承,不支持多继承
支持多层继承(集合体系)
继承时子父类成员之间的关系:
成员变量关系:
子类的成员变量与父类的成员变量名称相同时,使用变量的原则:就近原则,使用的是子类的成员变量(与多态中的成员变量关系结合理解);
在子类方法中访问一个变量的查找顺序(就近原则):
A.在子类方法的局部范围查找
B.在子类的成员范围找
C.在父类的成员找
构造方法关系:
子类中所有的构造方法(无论带参与否)默认都会访问父类的无参构造
子类中所有的构造方法的方法体第一句语句默认是super(),为了父类数据的初始化
若父类没有无参数构造,那么
A.子类需要通过super(...)去调用父类的其他带参的构造方法
B.子类需要使用this(...)去调用本类的其他构造方法,且这个构造方法中使用super(...)调用了父类的其他带参的构造方法;
this(...)和 super(...)第一要放在构造方法的第一句,否则多次初始化父类数据
成员方法关系:
子类的成员方法与父类的成员方法的返回值、方法名、参数列表都相同(这种情况也叫方法重写)时,使用的是子类的方法;
重写:子父类中,成员方法的返回值、方法名、参数列表都相同时,子类的成员方法覆盖父类的成员方法,调用时使用的是子类的方法体,若还想调用父类的方法体则使用super.(...);
子类重写父类方法时,子类的成员方法权限 >= 父类的成员方法权限
注意事项:
子类只能继承父类所有的非死有成员
子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
不要为了部分功能去继承
类加载时,发现其有父类先加载父类,再加载子类,静态修饰内容是随着类的加载而加载的,所以当子父类中有static时,static是最先加载的;类中有静态代码块、构造代码块、构造方法,则执行顺序是:
静态代码块>构造代码块>构造方法
看代码写结果:
public class ExtendsDemo2 extends X{
Y y = new Y();
ExtendsDemo2() {
System.out.println("Z");
}
public static void main(String[] args) {
ExtendsDemo2 z = new ExtendsDemo2();
}
}
class Y {
Y() {
System.out.println("Y");
}
}
class X {
Y y = new Y();
X() {
System.out.println("X");
}
}
考点:
类的初始化过程,
子父类的初始化(分层初始化):先初始化父类后初始化子类
super仅仅表示要先初始化父类数据,再初始化子类数据
结果:YXYZ
看代码写结果:
package extends demo
public class ExtendsDemo3 {
public static void main(String[] args) {
B b = new B();
}
}
class A{
static{
System.out.println("父类 ---- 静态代码块");
}
{
System.out.println("父类 ---- 代码块");
}
A(){
System.out.println("父类 ---- 构造方法");
}
}
class B extends A{
static{
System.out.println("子类 ---- 静态代码块");
}
{
System.out.println("子类 ---- 代码块");
}
B(){
System.out.println("子类 ---- 构造方法");
}
}
考点:
静态代码块>构造代码块>构造方法
子父类:分层初始化
结果:
父类 ---- 静态代码块 //随着类的加载而加载
子类 ---- 静态代码块 //随着类的加载而加载
父类 ---- 代码块 //先初始化父类
父类 ---- 构造方法 //先初始化父类
子类 ---- 代码块 //后初始化子类
子类 ---- 构造方法 //后初始化子类
看代码写结果:
public class ExtendsDemo4 {
public static void main(String[] args) {
D d = new D();
d.show();
}
}
class C{
public String name = "我是 --父类--的name";
}
class D extends C{
public String name = "我是 --子类--的name";
public void show(){
String name = "我是 --局部变量--的name";
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
考点:this和super
结果:
我是 --局部变量--的name
我是 --子类--的name
我是 --父类--的name
多态
多态前提和提现:
有继承关系
有方法重写
有父类引用指向子类对象
父 f = new 子();
多态时子父类成员之间的访问特点:
成员变量的访问特点:编译看左边,运行看左边
/**
* 多态中 成员变量的访问特点:编译看左边,运行看左边
*/
public class ExtendsDemo1 {
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.num2);//20
Father child1 = new Child();
//父类引用对象,取父类中的值:运行看左边
System.out.println(child1.num2);//200
//报错:编译看左边,Father中无num变量,所以编译不通过
System.out.println(child1.num1);
GrandFather child3 = new Child();
//祖父类引用对象,取祖父类中的值:运行看左边
System.out.println(child3.num2);//2000
}
}
class GrandFather {
public int num2 = 2000;
}
class Father extends GrandFather {
public int num2 = 200;
}
class Child extends Father {
public int num1 = 10;
public int num2 = 20;
}
构造方法的访问特点:
创建子类对象的时候,访问父类的构造方法,对父类数据初始化
成员方法的访问特点:编译看左边,运行看右边
package duo tai
/**
* 多态中 成员方法的访问特点:编译看左边,运行看右边
*/
public class DuoTaiDemo2 {
public static void main(String[] args) {
Child1 child = new Child1();
//运行看右边:输出的是子类对象中的method方法
child.method();//20
child.show();//20
Father1 child1 = new Child1();
//运行看右边:输出的是子类对象中的method方法
child1.method();//20
//编译看左边:Father类中没有此方法,所以报错
child1.show();
GrandFather1 child3 = new Child1();
//运行看右边:输出的是子类对象中的method方法
child3.method();//20
}
}
class GrandFather1 {
public int num2 = 2000;
public void method() {
System.out.println(num2);
}
}
class Father1 extends GrandFather1 {
public int num2 = 200;
public void method() {
System.out.println(num2);
}
}
class Child1 extends Father1 {
public int num1 = 10;
public int num2 = 20;
public void method() {
System.out.println(num2);
}
public void show() {
System.out.println(num2);
}
}
静态方法的访问特点:编译看左边,运行看左边
package duo tai
/**
* 多态中 成员方法的访问特点:编译看左边,运行看左边
*/
public class DuoTaiDemo3 {
public static void main(String[] args) {
Child2 child = new Child2();
//运行看右边:输出的是子类对象中的method方法
child.method();//子类---静态
Father2 child2 = new Child2();
//运行看左边:输出的是父类对象中的method方法
child2.method();//父类---静态
//编译看左边:Father类中没有此方法,所以报错
child2.show();
GrandFather2 child3 = new Child2();
//运行看右边:输出的是祖父类对象中的method方法
child3.method();//祖父类---静态
}
}
class GrandFather2 {
public static void method() {
System.out.println("祖父类---静态");
}
}
class Father2 extends GrandFather2 {
public static void method() {
System.out.println("父类---静态");
}
}
class Child2 extends Father2 {
public static void method() {
System.out.println("子类---静态");
}
public void show() {
System.out.println("子类---静态---show");
}
}
多态时子父类成员之间的访问特点的内存图解:
/**
* 多态时子父类成员之间的访问特点的内存图解
*/
public class DuoTaiDemo4 {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.age);//40
f.show();//子类--show
}
}
class Fu{
public int age = 40;
public Fu(){}
public void show(){
System.out.println("父类--show");
}
}
class Zi extends Fu{
public int age = 20;
public Zi(){}
public void show(){
System.out.println("子类--show");
}
public void method(){
System.out.println("子类--show");
}
}
多态时子父类成员之间的访问特点的内存图解.png
多态好处:
提高了代码的维护性(继承)
提高了代码的扩展性(多态)
多态弊端:
不能使用子类特有功能(编译看左边)
若要使用则需要强制转换(对象转型):
向上转型(把子变父):Fu f = new Zi();
向下转型(把父变子):Zi z = (Zi)f;//Zi与Fu是有关系的(子父类,接口实现类)
对象转型图解
对象转型图解
多态几种变现形式:
A:具体类多态(开发中很少遇到)
B:抽象类多态(常用)
C:接口多态(最常用)
抽象类
概述:
抽象方法:不是具体的功能成为抽象的功能,那么此方法就是抽象方法
抽象类:一个类中如果有抽象方法,则该类必须是抽象类;
抽象类特点:
A:必须使用abstract关键字修饰
B:抽象类中不一定有抽象方法,但是抽象方法的类必须定义为抽象类
C:抽象类实例化是通过其子类实例化的(多态),本身不能通过new实例化,因为其不是具体的;
抽象类不能通过new实例化,那么构造方法的意义:抽象类的构造方法是用于子类访问父类数据的初始化;
D:抽象类的子类:
1) 如果子类不能重写抽象类的全部抽象方法,那么此子类还是抽象类
2) 如果子类重写了所有的抽象方法,那么此子类是一个具体的类;
抽象类成员的特点:
成员变量:与普通类的成员变量一样
构造方法:用于子类访问父类数据的初始化;
成员方法:既可以是抽象的,也可以是非抽象的
抽象方法: 强制要求子类做的事情
非抽象方法: 子类继承的事情,提供代码的复用性
注意事项:
一个类如果没有抽象方法可以定义为抽象类吗?意义何在?
可以:作用是不让创建对象
abstract和private、final、static不能共存
private:冲突;
private和abstact同时修饰方法修时,
private修饰不能被继承继而不能重写,
而abstract修饰的方法是要求必须重写的;
final:冲突;
与private一样的道理;
static:无意义;
当static和abstact同时修饰方法时,
方法是没有方法体的,
static随着类的加载而加载,加载一个没有方法体的方法,无意义;
接口
概述:
抽象方法:不是具体的功能成为抽象的功能,那么此方法就是抽象方法
抽象类:一个类中如果有抽象方法,则该类必须是抽象类;
接口特点:
A:使用interface关键字表示
格式:interface 接口名{}
B:接口不能通过new实例化,只能通过多态来实例化
C:接口的子类:
可以是抽象类,但没有什么意义
可以是具体的实现类,使用implement表示
格式:class 类名 implement 接口名{}
可以是接口,使用extends(java源码 interface BeanContext extends Collection)
接口成员特点:
成员变量:只能是常量,并且是静态的
默认修饰符是:public static final
构造方法:无
接口实现类中的构造方法的super(),是Object的构造方法
查看API可知Object的构造只有一个:无参数构造,所以子类的构造默认都有一个无参的super();
成员方法:
1.8版本之前:只能是抽象方法,默认修饰符是 public abstact
1.8版本:可以出现带方法体的方法,主要有2种:默认方法和静态方法
/**
* java1.8版本接口的新特性:可以定义带有方法体的方法,default和static
*/
public class InterfaceDemo1 {
public static void main(String[] args) {
Interface1 inter = new Interface1Impl();
inter.getNameDefault();
inter.show();
Interface2.getNameStatic();
}
}
interface Interface1{
void show();
default void getNameDefault(){
System.out.println("接口中的带有方法体的默认方法");
}
}
interface Interface2 extends Interface1{
void method();
static void getNameStatic(){
System.out.println("接口中的带有方法体的静态方法");
}
}
class Interface1Impl implements Interface2 {
@Override
public void show() {
System.out.println("接口实现类--show");
}
@Override
public void method() {
System.out.println("接口实现类--method");
}
}
类、抽象类、接口之间:
抽象类与接口的区别:
设计理念的区别:
抽象类:继承关系,体现的是"is a "是的关系。
抽象类中定义的是该继承体系的共性功能。
接 口:实现关系,体现的是"like a "。
接口中定义的是该继承体系的扩展功能。
成员区别:
抽象类:
成本变量:变量、常量都可以
构造方法:有
成本方法:普通方法和抽象方法都可以
接 口:
成本变量:只能使常量
构造方法:无
成本方法:1.8前只能抽象方法,1.8以后普通方法是default和static
类与类,接口与接口,类与接口各自之间的关系
类与类:继承关系,单继承可以多层继承
类与接口:实现关系,可以多实现(多个接口使用“,”隔开)
接口与接口:继承关系,单继承也可以多继承(多个接口使用“,”隔开)
类、抽象类、接口作为形式参数时:
类:传递的是该类的对象(地址值)
抽象类:需要的是该抽象类的子类对象(地址值)
接口:需要的是该接口的实现类对象(地址值)
类、抽象类、接口作为返回值类型时:
类:返回的是该类的对象(地址值)
抽象类:返回的是该抽象类的子类对象(地址值)
接口:返回的是该接口的实现类对象(地址值)
内部类:
概述:
定义在另一个类中的类
内部类的访问特点:
内部类可以直接访问到外部类中的成员,包括私有
外部类想访问内部类中的成员,需要创建内部类的对象
内部类的位置:
成员内部类:在类的成员位置
使用: 外部类.内部类 对象名 = 外部类对象.内部类对象;
局部内部类:
/**
* 在内部类中访问外部类:使用 外部类.this
*/
public class InnerDemo1 {
public static void main(String[] args) {
Outer.Inner inner = new Outer().new Inner();
inner.show();
}
}
class Outer{
public int num = 10;
class Inner{
public int num = 20 ;
public void show(){
int num = 30;
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(Outer.this.num);//10
}
}
}
局部内部类:定义在方法内的类
使用:可以创建内部类的对象,通过内部类对象操作内部类成员
局部内部类访问局部变量的注意事项?
/**
* 局部内部类访问局部变量的注意事项:局部变量需要使用final修饰
* 1.8之前:局部内部类使用局部方法,书写时使用final修饰,
* 编译后的class文件中使用变量位置是变量值
* 1.8版本:局部内部类使用局部方法,书写时不使用final修饰,
* 编译后的class文件中自动给变量加入final修饰,且使用变量位置还是变量名
* 说明:不确定是1.7之前还是1.8之前
*/
public class InnerDemo2 {
public static void main(String[] args) {
Outer1 inner = new Outer1();
inner.show();
}
}
class Outer1 {
public void show() {
//应该使用final修饰,但是1.8没有报错呀?
//查看class文件发现:int num = 30; 变成了 final int num = 30;
int num = 30;
class Inner1 {
public void method() {
/*
* 查看class文件发现:num还是num,而不是30;
*/
System.out.println("局部内部类--method"+num);
}
}
Inner1 inner1 = new Inner1();
inner1.method();
}
}
匿名内部类
内部类的简化写法(类似匿名对象,没有名字)
匿名内部类的格式:
new 类名或接口名(){重写方法};
匿名内部类的本质是什么?
是一个类的子类或接口实现类或抽象类的具体类的匿名对象
在堆中开辟空间,只是这片空间没有与栈内存有相互关联;
/**
* 匿名内部类:是一个类的子类或接口实现类或抽象类的具体类的匿名对象
*/
public class InnerDemo3 {
public static void main(String[] args) {
Outer3 out = new Outer3();
out.method(40);;
}
}
class Outer3{
public void method(int num){
new Inner3(){
@Override
public void show() {
System.out.println("重写 --- show = " +num);
}
}.show();
Inner4 i = new Inner4(){
@Override
public void show() {
System.out.println("重写 --- show = " +num);
}
@Override
public void show2() {
System.out.println("重写 --- show2 = " +num);
}
};
i.show();
i.show2();
}
}
interface Inner3{
public abstract void show();
}
interface Inner4{
public abstract void show();
public abstract void show2();
}
匿名内部类在开发中使用:
/**
* 匿名内部类:是一个类的子类或接口实现类或抽象类的具体类的匿名对象
*/
public class InnerDemo4 {
public static void main(String[] args) {
Person out = new Person();
out.method(new Inner5(){
@Override
public void show() {
System.out.println("匿名内部类在开发中的常见使用" );
}
});;
}
}
interface Inner5{
public abstract void show();
}
class Person{
public void method(Inner5 inner){
inner.show();
}
}
看结果写代码
/**
* 看接口写代码
*/
public class InnerDemo5 {
public static void main(String[] args) {
Person1.method().show();
}
}
interface Inner6{
public abstract void show();
}
class Person1{
//补齐代码
public static Inner6 method(){
return new Inner6(){
@Override
public void show() {
System.out.println("hello world" );
}
};
}
}
考点:Person1.method():方法静态
Person1.method().show() 链式编程
匿名内部类
参考书籍视频:
-学习视频:黑马程序员_Java基础视频-深入浅出精华版
-阅读书籍:Java核心技术 卷I(机械工业出版社)
API1.8;;;


