面向对象02

DAY11

  • 访问权限修饰符

访问权限修饰符的访问控制分为2个层面:

修饰类中的成员(成员变量&成员方法)——控制类中的成员,对其他类的可见性

修饰类——通常用来限定类库中的类(自定义数据类型),对于外部使用者的可见性

 

对类中成员的访问限制,有4种:public、protected、默认权限、private(成员方法和成员变量在访问权限上是相同的)

 public

任意类都可以访问,实际上就是没有限制访问权限

protected

同包中的其他类,和不同包的(可见)子类均可见

默认权限

同包中的其他类可见

private

仅对同类中的其他成员可见

 

类的访问权限

public:使类具有公共访问权限

protected:不能用来修饰类(并非所以类)(通过子类对象可以跨包访问到)

默认权限:只有同包中的类,能访问默认权限的类

private:不能修饰类(并非所有类)

 

  • 封装

一种信息的隐藏技术

类
    成员变量(一定要考虑访问权限)
    构造方法
        无参构造方法
        带参构造方法
    成员方法
// 针对private成员变量,专门定义了public的方法,让外部能够访问私有成员变量的值 getxxx()//让外部读取到私有成员变量的值 setxxx()//让外部通过该方法,修改私有成员变量的值

为了清楚的get和set方法分别获取和改变的是哪个成员变量的值,常用命名规则:

成员变量名字:xxx

返回值 getxxx()

void setxxx(修改参数目标值)

 

提供get/set方法的好处:

  1. 可以满足某些场景下需要访问私有成员变量的值
  2. 通过定义get和set方法,来实现对私有成员变量的读写分离
  3. 因为,一旦一个成员变量被private修饰,别人就无法直接 对象名.访问。只能通过set(),此时我们在set方法中,就可以自己通过代码控制别人的修改

给成员变量赋值的方式

1.无参构造方法+setxxx()

Student st = new Student()

st.name = setName()

2.带参构造方法

 

  • 继承

父类(基类,超类)

子类(派生类,导出类)

继承的语法:class 子类名 extends 父类名{}

不同数据类型之间“is  a”的关系

a.一种类型(类) 继承 另外一种类型(类)

b.从数据类型看,可以把子类类型当做父类类型

  从数据类型的角度,数据类型:一个数据集合和基于这个数据集合的一组操作

当子类继承父类,子类拥有了父类中定义的成员(父类中的数据集和基于数据集的操作)

 

继承的优点:

  1. 代码复用(方法,类)
  2. 提高了代码的可维护性(这是一把双刃剑)--通过减少了冗余代码
  3. 弱化了Java中的类型约束(可以让父类引用指向子类对象)(多态的前提之一)
Human human = new Human();
Student student = new Student();
//一个子类对象就被看成了一个父类对象
huamn = student;
class Human{}
class Student extends Human{}

继承的缺点:

父类的修改可能会出现在所有的子类当中(我们无法选择这些修改可以反应在哪些类中,不可以反应在哪些类中)

 

继承的局限性:

Java继承的特点:在Java只能实现单重继承

单重继承:简单来说Java的extends关键字后只能跟一个

class A{}
class B{}
class C extends A,B{} (x)//不能同时继承过个类

//但是可以有层次的继承
class A{}
class B extends A{}
class C extends B{}//c中继承了A和B

注意事项:

  1. 子类不能继承父类的构造方法
  2. 子类只能访问父类所有非私有的成员(成员方法和成员变量)(例如有private的成员,那么能被继承,但是不能访问)

子类对象的内存映像:

对一个子类对象而言,其内存中的数据,分成了两部分:

1.从父类中,继承下来的成员变量的值

2.子类中,自己定义的成员变量的值

在给这两部分数据赋初值时,java语言的结论是,一定是先父后子

 

子类对象的初始化问题(子类中成员变量赋予初值先后顺序)

实现先父后子的核心思路:

  1. 首先,构造方法本身的功能,就是初始化对象成员变量值的
  2. 所以,显然对我们只需要保证,父类的构造方法先于子类的构造方法运行
  3. 如何让父类构造方法,先于子类构造方法运行(在子类的构造方法的第一条语句的位置,先去调用父类)

子类的初始化方式

  1. 子类对象的隐式初始化方式(JVM来自动保证父类构造方法先执行)
  2. 子类对象的显式初始化方式(coder通过代码的方式保证父类构造方法先执行)

 子类对象的隐式初始化:

1.了解对象的隐式初始化

a.当父亲提供了默认的构造函数(无参构造方法)

b.且子类的构造方法中,没有显示调用父亲的其他构造方法

c.则在执行了类的构造方法之前,会(JVM)自动执行父亲的构造方法(无参构造方法 )

 

子类对象的显式初始化方式:

核心问题:如何在Java语法层面,显式的调用父类的构造方法

super关键字:super代表父类对象的引用

如何保证先调用父类构造方法执行,后执行子类构造方法?

只需要在子类构造方法第一条语句的位置,通过super调用父类的构造方法即可

 

 

注意事项:

  1. 必须保证子类每个构造方法都要满足子类对象的初始化(即如果子类中有多个构造方法,每个都要保证显示初始化的执行流程)
  2. 在子类构造方法中,通过super调用,父类构造方法时,该super语句必须出现在子类构造方法的第一条语句的位置
  3. this()(调用当前类的构造方法)和super()(调用父类构造方法)都可以卸载构造方法的方法体中,他们都需要在第一条的位置,他们不能共存
class ExplicitFather{
    int fatherI;
    public ExplicitFather(int fatherI){
        this.fatherI = fatherI;
    }
}
class ExplicitSon extends ExplicitFather{
    int sonI;
    public  ExplicitSon(int sonI, int fatherI){
        super(fatherI);
        this.sonI = sonI;
    }
    public ExplicitSon(){
        super(23);
    }
}

DAY12

父类域的隐藏(仅仅发生在子类当中)

1.在子类中是否可以定义和父类中同名的成员变量?————可以

2.那么在子类中访问这个同名变量,究竟访问到的是父类对象中的值,还是子类对象中的值?——子类中定义的同名成员变量值

如果是在父类中访问呢?——父类方法访问到的是父类中定义同名成员变量的值

3.是否可以在子类对象中,同时访问到子类对象和父类对象中的同名成员变量的值?

——可以,在子类方法体中,可以通过super.关键字,访问到父类定义的同名成员变量值

 

在父类子类中,如果定义了同名成员变量,记住以下结论:

1.如果调用的是子类方法,通过变量名访问同名成员变量,访问到的就是子类中定义的同名成员变量

2.如果调用的是父类方法,通过变量名访问同名成员变量,访问到的就是父类中定义的同名成员变量

 

对于子类中的方法,在子类对象上访问成员变量值的时候,有一个查找规则:当我们在子类的方法体中,访问子类对象的成员变量值的时候

1.首先,在子类对象中,查找子类自己,定义的成员变量中有没有目标成员变量

2.如果找到,就直接访问

3.如果在子类对象上,在子类自己定义的成员变量中,没找到目标变量,则会自动在父类对象上,查找目标变量值并访问

 

  • 方法的覆盖或重写(override)

1.子类中能否能定义和父类方法声明一模一样的方法?——可以

2.如果可以,那么在子类对象中访问这个一摸一样的方法,究竟访问到的是父类对象中的方法,还是子类对象中的方法?——子类中定义的方法

如果是在父类中访问呢?——子类中定义的方法

3.是否可以在子类对象中,同时访问到子类对象和父类中方法声明一模一样的方法呢?——可以,通过super.关键字 可以在子类中访问到父类中定义的成员方法

 

当我们在子类对象上调用方法,该方法在运行时,怎么确定运行哪个方法?

1.当方法执行的时候(如果子类方法调用了其他方法)

  优先在子类方法中找目标方法,如果子类中找到了目标方法,执行子类中定义的目标方法

2.如果说,在子类中,没有找到目标方法,接着到父类中找目标方法,如果找到就执行父类中定义的方法

使用场景:用来在子类中,修改父类方法的方法实现(并非真的修改父类中的方法)

方法覆盖的条件:

主要看的是子类和父类的方法声明部分:访问权限  返回值   方法签名(方法名+参数列表)

1.方法声明的访问权限的条件

  并非子类和父类方法的访问权限要相同,只要子类方法的访问权限不小于父类方法的访问权限

2.方法返回值

1)基本数据类型的方法返回值:子类必须和父类的方法返回值类型相同

2)引用数据类型

a.子类父类返回值类型相同

b.子类方法返回值类型 是 父亲方法返回值类型的 子类类型(因为可以把子类类型看做是父类类型)

class A{}
class B{}
class C extends A{}//C是A的子类

class Father{
    protected int testAccess(){
    System.out.println("father access");
    return 0;
}
public A testReferenceReturn(){
    return null;
    }
}
class Son extends Father{
    @Override
    public int testAccess(){
    System.out.println("son access");
    return 0;
    }        
    @Override// 测试有没有实现覆盖的注解@override
    public C testReferenceReturn(){
    return null;
    }
}

 

3.方法签名(数据类型的角度)

子类方法的方法签名必须和父类一样

注意事项:父类中不是所有的方法都能被子类覆盖

1.父类中私有方法不能被重写

2.父类中静态方法不能被重写

3.被final修饰

 

  • final关键字

final可以修饰类,变量,成员方法

  1. 修饰类,类不能被继承
  2. 修饰变量,变量就成了常量,只能赋值一次
  3. 修饰方法,方法不能被重写

 

final修饰变量时分为两种情况:

修饰局部变量:必须在使用局部变量之前,初始化一次局部变量的值,且仅一次

修饰成员变量:(不包括jvm赋予的默认初值 )可以在定义时进行初始化,也可以选择在构造方法中初始化

 

  • 多态

某一个事物,在不同时刻(或条件下)表现出的不同状态(行为), 对于成员变量没有所谓的多态

 

多态实现的前提条件:

  1. 继承
  2. 方法覆盖
  3. 父类引用指向子类对象

 多态成员的访问特点

Animal animal =  new Dog();

成员变量:编译看左边,运行看左边(实际运行的效果,由引用变量的类型来决定)

成员方法:编译看左边,运行看右边

解释:

1.编译看左边:父类引用指向子类对象,此时编译看左边是在说,通过引用变量可以访问到的子类成员的范围,是由引用类型来决定的 

a.迄今为止,我们都是通过一个中间人即引用变量,间接访问堆上对象

b.也就是说,只有通过引用变量,我才能访问到堆上的对象

2.对于成员变量的运行看左边

一个对象属性(成员变量)和行为,一个对象的属性(成员变量表示),表示了一对象的外貌在多态中,此时对于子类对象而言,把子类对象赋值给父类类型的引用,就相当于给子类对象披上了一个父类类型马甲,因此,该子类对象开起来,就是一个父类对象(外貌特征表现出的就应该是父类的外貌特征)

3.对于成员方法的,运行(结果)看 右边(多态)

就是说对于成员 方法而言,通过引用变量,实际访问到的行为(方法),是由引用实际指向的对象来决定的

 

 

多态的优点:

提高了程序的维护性(由继承保证)

提高了程序的扩展性(由多态保证)

多态的弊端:

1.无法通过父类类型的引用,去访问子类特有的方法

解决方案:引用变量的类型转换(父类和子类)

a.子类类型的引用变量—>父类类型的引用变量(向上转型)     编译器天然允许

b.子类类型的引用变量—>父类类型的引用变量(向下转型)     默认编译器不允许(因为一旦赋值不恰当,可能引起错误)

对于引用变量,我们也可以强制类型转化

子类类型 引用变量  = (子类类型)父类型的引用变量

DAY13

1.

2.ClassCastException,为了成功的完成强制类型转化,必须想办法判断,animal父类引用指向的究竟是不是Duck对象

instanceof关键字(运算符):判断目标是否是指定类型的对象   运算类型:boolean

语法格式:对象名(对象的引用变量)  instanceof    实例类型(目标类的类名)

引用变量:不管什么类型的 引用变量,都可以被赋值null,如果null instanceof任意一个类 ——false

  • 抽象类(abstract)

为了让方法声明单独存在(声明有这样的行为),必须在声明中加入abstract关键字,被abstract修饰的方法叫做抽象方法

注意:包含抽象方法的类,必须是抽象类,同时,抽象类可以不包含抽象方法

 

语法:

类:abstract class 类名{}

public abstract void eat{};抽象方法只有方法声明,没有方法体

特征:

1.抽象类不能直接实例化:new 抽象类

但是抽象类可以间接实例化

抽象类类型 引用 = new 具体子类();

2.抽象类的子类:

a.可以是具体类(子类必须覆盖实现抽象父类中,所有的抽象方法)

b.抽象类的子类可以是抽象类(当子类没有覆盖并实现,抽象父类中所有的抽象方法)

注意事项:

a.有抽象方法的类一定是抽象类

b.抽象类不一定有抽象方法

 

抽象类成员特点:

构造方法:同普通类(不能实例化,为什么有构造方法?)

成员变量:同普通类

成员方法:可以是抽象方法(只是为了声明类中有这样的行为,而无法确定具体行为的实现),

也可以是非抽象方法:作用是用来做一个代码复用

 

抽象类不能实例化,为什么有构造方法?

1.因为抽象类中,可以像普通类一样,定义成员变量,而这些成员变量,在创建子类对象的时候,依然是初始化

2.在子类独享中,抽象父类中定义的成员,它们值的初始化,交给抽象父类的构造方法来完成(各司其职)

 

abstract不能和以下关键字共存

1.private不能被覆盖

2.final不能被覆盖

3.static不能被覆盖

对于为什么冲突的理解:

1.abstract定义抽象方法,对于抽象方法而言,如果在代码中要使用,其实永远是通过多态,覆盖实现的抽象父类的抽象方法

2.而被private,final,static关键字修饰的方法,都不能在子类中被覆盖,而是意味着这些方法,无法在程序中运行

 

DAY14

  • 接口

解决类的单重继承限制

接口的语法

1.接口用关键字interface表示:interface 接口名{}

2.在java语言中 interface也可以表示一种数据类型

a.类和接口都可以用来表示数据类型(类和接口是地位对等的 ,只不过他们的侧重点不一样)

主要从操作(行为)描述:

b.类定义的一个数据集合基于这个数据集的一组操作(行为),类所描述的这一组行为,他们是有关系的(间接),都可以访问同一个数据集合

c.接口表示数据类型,侧重于描述,一组具有特殊功能的行为,这些行为可以完全没有任何关系,接口中的方法,他们的关系比较松散

3.类实现接口和implements表示

类与类之间可以有一种关系:继承

类和接口,可以有实现关系,实现关系用implements表示(一个类可以实现接口)

 

类和接口的实现关系(类可以实现接口):实现关系其实是一种实质上的继承关系

格式:class类名implements 接口名{}

 

接口的特征:

4.接口不能直接实例化(接口中对于方法只能有抽象方法)(jdk7以前)

a.不能直接实例化一个接口(new接口)

b.接口可以间接实例化

接口类型的引用  = new 接口实现子类()

 

接口的特点:

1.无构造方法

2.成员变量:只能是常量,修饰符public static final

成员方法:只能是抽象方法,修饰符public abstract

5.接口的子类

a.可以是抽象类

b.也可以是具体类

 

Java语言实现了多重继承:

1.接口与接口之间可以实现多重继承

2.同时,一个类可以实现多个接口

完整的类定义的语法

class 类名extends 另一个类的类名 implements 接口1 ,接口2,。。。接口n{}

 

抽象类和接口的比较

  抽象类 接口
成员区别

变量

有抽象方法

非抽象方法

自定义常量(静态)

抽象方法(jdk7以前成立)

关系区别

类和类——继承,单继承

类与接口——实现,单实现(实现一个接口),多实现(实现多个接口)

接口与接口——继承,单继承,多继承

设计理念区别

1.被继承体现的是is a的关系

1)抽象类可以被其他类继承,而且子类只能extends一个类

2)抽象类被子类继承以后,子类和抽象类的关系是is a

2.共性的功能

1.被实现体现的是like a的关系

1)一个类可以同时多个接口(实现也是一种是实质上的继承关系)

2)类实现接口之后,类和接口的关系用like a 来描述

2.功能的扩展

说明is a 和like a 差别:

is a :一个子类,只能继承一个父类,继承在最理想的使用情况下,是子类不自己定义自己成员,此时在理想情况下,其实父类和子类,是完全相互替代的  父类引用/子类引用 = new 子类对象();

like a :因为一个类可以同时实现多个接口,每个接口只表示子类中的一部分成员,通过一个接口只能看到一个类的冰山一角

  •  内部类

定义在其他类内部的类

按照内部类在类中定义的位置的不同,可以分为如下两种格式:

成员位置(成员内部类)

局部位置(局部内部类)

内部类的访问特点:

内部类可以直接访问外部类的成员,包括私有

外部类要访问内部类的成员(甚至私有成员变量和成员方法也可以访问到),必须创建对象

 

成员位置内部类,也依赖于外部类对象而存在

————————————————————————如何在外部类的外部,创建一个内部类对象

内部类的访问语法(外部类的外部访问)

外部类名.内部类名 对象名 = 外部类对象.内部类对象

//1.定义指向内部类的引用变量
MemberOuter.MemberInner obj;
//2.创建指向内部类的引用变量
MemberOuter outer = new MemberOuter;
//3.在外部类对象上,创建外部类对象
obj = outer.new MemberInner();
//一步完成

MemberOuter.MemberInner obj = new MemberOuter().new MemberInner();

成员位置内部类用static修饰,就不依赖与外部类对象而存在了

静态内部类:整个类都是一个静态上下文

在外部类的外部,创建一个静态成员位置内部类 

外部类名.内部类名 对象名 = new外部类名.内部类名();

局部内部类:定义在类中,方法体中的类

内部类的 访问特点:

1.内部类可以直接访问外部类的成员,包括私有

2.外部类要访问内部类的成员,必须创建对象

注意:局部内部类对象的创建要简单的多,因为局部内部类只能在定义该类的方法体中才能被访问

先定义局部内部类,然后才能创建对象

局部内部类特点:

可以访问到方法体中的局部变量

但是在局部内部类中,我们只能访问方法体中的自定义常量——原因在于生命周期的冲突

局部内部类对象 vs 局部变量

1.局部变量的生命周期,随着方法的执行结束,即栈帧销毁,而从内存消失

2.但是对于局部内部类对象而言,存储在堆上。对象的销毁和方法栈帧,没有直接关系,简单来说就是方法运行完了,局部变量不存在了,但是对象还在

3.所以,如果方法运行完毕之后,还有人可以使用这个对象,那么就可以通过该局部内部类对象去访问这个局部变量,而此时,局部变量早已随着方法的执行完毕,从内存中消失了

如何解决这个冲突——final

effectively final 的变量:定义之后在方法体中,只被赋值一次且仅赋值一次的变量

 

4.匿名内部类 对象

创建匿名内部类对象的前提:存在一个类或接口(这里的类可以是具体类也可以是抽象类)

匿名内部类对象:

new 类名或接口名(){重写方法}

本质:是一个继承了类或者实现了接口的子类匿名对象

匿名对象:创建一个对象的时候,没有让引用变量指向该对象(没有给对象起名字)

匿名对象我们只能在创建这个对象的时候引用一次,且仅一次

posted @ 2020-05-20 15:23  Poker1996  阅读(132)  评论(0编辑  收藏  举报