单例模式设计
1.设计模式罗列
创建型模式:关注类和对象创建过程;
结构性模式:关注类和对象的组合;
行为型模式:关注对象之间的通信过程;
(注:设计模式不是理论推导的结果,而是在解决实际问题过程中的经验总结)
2.单例模式
基本要求:某个类只能有一个实例对象;类中必须要有创建实例的方法,并向外提供实力对象;
具体实现要求:(1) 类的构造方法需要私有 (private,这样就不能在类外通过new来获取实例对象了);
(2)类需要有一个 静态(static)的私有 (private)的对象 (为了保证有且只有一个实例对象);
(3)提供一个共有的 静态(static)方法 ,用以创建、获取静态私有对象;
单例模式两种实现方案:饿汉式和懒汉式
饿汉式:在类加载的时候,就完成静态私有对象的实例化,是一种空间换时间的策略;
注:构造方法只执行一次,即在类加载过程中被调用一次;
懒汉式:在类加载时没有进行静态私有对象的实例化,只有用的时候才通过getInstance()方法去实例化,一种时间换空间的策略;
注:构造方法只执行一次,即在使用的时候,通过调用getInstance()方法时才执行;
饿汉式和懒汉式比较:
(1)饿汉式第一次使用时速度很快,但实例对象一直在哪儿,一直占着空间;懒汉式第一次使用时速度慢;
(2)饿汉式是线程安全的;懒汉式存在线程风险;( ** _ 先知道即可,后续深入了解_** )
总结:
具体哪些业务场景适合使用单例模式,需要逐渐摸索总结。
(
)
Java多态
1.Java多态(polymorphism)简述
生活中常有,同样的行为在不同的对象上会有不同的显示结果;而在Java中,多态意味着允许不同类的对象对同一消息做出不同的响应。
编译时多态:更多地通过同一个类中的方法重载体现(这个不是重点);
运行时多态: 一般需要,满足继承关系、父类引用指向子类对象这两个必要条件;(一般所说的多态即运行时多态)。
2.多态演示实例:向上转型
向上转型:
下面如:Animal two = new Cat(),Animal父类引用指向了子类Cat类型的实例,即把一个子类对象转型为一个父类对象,称为向上转型。向上转型也可称为隐式转型、自动转型。
向上转型是由小向大转;
转型后的【two】可以调用子类重写父类的方法,以及子类继承的父类的其他方法; 但无法访问子类新添的方法。
(20210708注:经过实测,父类变量指向子类引用后,其是不能调用父类的private私有方法的,因为子类不会获得父类的private私有方法。。。。自然,对于其他父类的、子类不能继承的东西来说,都是不可的)
注:
(20210708注:所以,应该能感觉到,【动态绑定机制】是java之所以能实现多态效果的,背后的支撑)
Comments:
(1)同一个父类的引用,如果指向不同的子类实例对象,就会调用不同子类中重写的方法。从而产生多态的效果。(20200914)
向下 转型
向下转型:子类引用指向父类实例对象;向下转型又称为:强制类型转换;
完美状态下的实例:two本身就是父类引用指向的子类对象( 这儿相当于原样转回。。。哪儿来的哪儿去 )
此时,可以调用子类继承的方法、子类继承的方法、 也可以调用子类新添的方法 。
但如果这样:
three是:Animal父类引用指向Dog子类对象;发现语法编译没报错,但运行时报错了,类型转换失败。
原因分析:只能如上例:原样返回式的向下转型
这样也是错的。子类引用指向父类会出现编译时错误;下图的例子会报运行时错误。
1.instanceof运算符的作用:转换前作判断,判断待转对象是否具有目标类的实例特征;
(一般,子类对象具有父类的实例特征;自然其具有本类的实例特征)
尤其在向下转型时使用(因为向下转型更容易出错);
1.向上转型:为什么需要向上转型
(20210708注:向上转型是java多态中的一个重要内容;然后,多态又是【编写易于 维护、扩展 的程序】的重要底层支撑。)
如方案1所示,当测试各个子类的eat()方法时,只需要写一个eatTest()方法即可,其会通过向上转型机制,决定去调用哪一个子类的eat方法。
但如果这样:
由上两个例子可以看到,采用向上转型的方案更加简单灵活。如后续再添加新的子类,也无需新增eatTest()方法。
2.向下转型:为什么需要向下转型
向下转型一般是为了重新获得因为向上转型而丢失的子类特性而存在;因此,通常在向下转型前常有向上转型,而向下转型通常也会结合instanceof一起使用;
借由向下转型,可以在灵活应用多态的基础上,同时兼顾子类的独有特征。
Comments:
(1)为什么需要,向上转型:我的理解,向上转型后,父类引用可以调用子类重写父类的方法,这样当需要新添功能时,可以新增一个(子)类即可,而不用更改原父类代码。(20200914)
抽象类
1.抽象类
为什么会有抽象类:
抽象类的写法:abstract关键字
Animal定义为抽象类后,Animal animal = new Animal();会报错;但,Animal one = new Cat()依旧是可以的。
抽象类或是,保证“逻辑意义”完美、提高java语言的简介和美观性,的一种辅助保障手段。
当父类定义成抽象类后,借由父类和子类的继承关系,可以限制子类设计随意性,同时又可以避免无意义父类的实例化。
这一点目前理解,可能和接口类似;
2.抽象方法
通过抽象类的理解,可以发现,父类仅仅是划定了一个大纲,具体方法的实现可以没有(即,抽象父类中没有方法提的方法,可以在子类中去具体实现)。即父类中的有些方法可以没有具体实现,即没有方法体。
这个时候,如果子类不重写eat()方法会报错。(除非将子类也定义成抽象类,并且子类中的eat()方法也定义成抽象方法。)
抽象方法可以认为是一种:子类必须具体实现父类抽象方法的一种提醒机制。
注:(1)包含抽象方法的类,一定得是抽象类;在一个抽象类中,可以没有抽象方法。
(2)根据abstract的设计原则可以发现,static,final,private都不能与abstract共同使用。
Comments:
(1)象抽象类这种,父类引用指向子类实例对象的做法,是多态一种明显的体现。(20200914)
(2)抽象方法,更加体现了,父类“指导性,大纲性”的特点。即,子类必须重写抽象父类中的抽象方法,而父类引用指向子类对象,又可以调用子类重写父类的方法。由此,契合多态,,降低了父类的多余的预先动作,降低了父类的复杂程度。(20200914)
( 20210708注:因为,目前感觉,自己在实际开发中很少使用抽象类,所谓【多态部分】大都是接口来实现的;所以,这儿暂时做了一个【 可以勉强安抚自己的说明 】:
抽象类在实际中的使用:参考自:【JAVA抽象类应用的多吗】,回答者是:【 Dr_Joseph 】
)
接口
1.为什么需要接口
Java中的类只支持单继承,即一个子类只能有一个父类。子类会具有父类的通用特征;
那么,如果我们希望在一个类中能够同时兼容多个不同类型的特征?
如果我们要求,不同的类型在他们无法拥有共同父类的前提下,仍然要包含有相同的特征?
此时,代码应该如何编写?
如:手机,和智能手表都能打电话和发短信,但很难将二者抽取一个公共的父类。因为手机和手表的功能是相互交叉、互有异同的。
但是,手机和手表之间又有相似的行为,由此可以引出接口,通过接口来建立这些相似行为之间的联系。
2.接口实现
接口中的方法,不能有方法体。(注意,这儿没有使用abstract,没有报错)
一个类实现一个接口使用:implements关键字;如下定义的Camera相机类
(注:implements:使生效,贯彻,执行,实施)
报错发现:一个类实现一个接口的时候,需要实现接口中的方法。
一个类可以同时继承一个类,和实现接口。如下定义的FourthPhone手机类
(注:继承父类:更偏重于“省事,不用重复书写某些代码”;实现接口:更偏重于“必须得这么做,必须得实际完成某些具体代码”)
测试:
注:如上,IPhoto ip = new FourthPhoto();接口引用指向实现类时,该ip只能调用接口中的方法,而不能调用FourthPhoto()类中自己定义的方法。 (经验证:一般的父类引用指向子类对象的情况下,其也只能调用父类中的方法,和子类重写父类的方法,而不能调用子类新增的特有方法)
即由此可以感觉到,我们可以通过接口来描述不同的类型具有相似的行为特征,从而建立关系之后,以接口引用指向实现类的方式来描述 不同的类型对于接口的行为的具体表现。( _ 即,接口仅倾向于不同类之间的共性_ )。 (20210708注:目前理解,这也是java多态的基础;这也是之所以java能开发【易于维护和扩展程序】的支撑;)
即同一个接口类型的引用,会因为其实际指向的对象类型的不同,而调用同一个名字的方法的不同的实现
Comments:
(1)接口更加体现了,多态的抽象类中的“父类”的“大纲华,简单化”,接口中的方法都是不能有方法体的抽象方法。 接口也体现了多态的特性。 同时,接口的多继承,也扩展了Java的灵活性。(20200914)
(2)抽象类到接口的关系,类似于,从特殊到一般的关系。相对于一般的Java类,抽象类解放了一般类,避免了一般类中不必的、可能没有实际逻辑意义的、预设性的代码;;;而接口更加强化了抽象类的特征,更加见简略了代码,同时由于多继承的关系增加了程序的灵活性。一般类、抽象类、接口,均能体现Java语言的多态特性,而且在这个递进演化过程中,其越来越简洁、越来越明显。(20200914)。
(3)20210708注:抽象类和接口各有用武之地;目前的理解是:接口更偏重于在多态上的应用,,抽象类更偏向于【把很多共用的方法和属性写在一个抽象类中】;
1.接口中抽象方法的tips
(1)接口中的抽象方法可以不写abstract,即接口中的方法默认为抽象的;即接口中的方法不能有方法体;
(2)接口中的方法默认访问修饰符为public(很容易理解,一个需要被其他类访问并实现的接口,其方法理应设为public)(这儿更多的可以从,Java的设计合理性出发去理解。)
2.接口中的常量
在一般类中,定义常量:
在接口中,定义常量时,可以省略public, static和final,即接口中系统默认有这三个修饰符。(常量的修饰符访问修饰符只能是public)注:接口中定义常量需要初始化,否则报错。 (20210708注:为什么接口中的常量要初始化:目前个人的理解是:接口中的常量是被static和final修饰的,因为是static修饰的,所以这个常量是隶属于类(或者接口)的,然后,因为final修饰,所以这个常量只能被修改一次,,,,所以,这个常量显然不能设计成【通过接口的实现类的对象去赋值的样子】;;;所以这个常量必须要在类加载的时候被初始化,所以无论是出于设计合理性还是背后的要求,都需要在接口中初始化这个常量;)
注:使用ip.也可以访问接口中定义的常量。但推荐使用接口名.的方式
3.接口应用中的常见问题
(1)当父类实现接口,子类继承父类后,是否也需要实现接口中的全部方法呐?
如果父类未实现接口中的全部方法,则父类需要设置为抽象方法,子类如果不想继续抽象,则需要实现相关未实现方法,否则将继续为抽象类。
(2)接口可以在不同的包中创建,可以根据业务设计需要单独创建接口包。需要跨包调用接口时,需设置可访问接口为public访问权限,然后参照类的跨包调用方式,import接口即可;
(3)如上实例中:接口引用指向实现类的实例的对象时,是一种体现多态的常见处理方式。
Comments:
(1)建议以后能看下Java编程思想。就可以对为什么抽象类和接口不能实例化,之类的问题有深入了解。
1.为什么需要默认方法
设想这样一种情况:有接口InterA,其中有两个方法a()和方法b();类ClassB实现了接口InterA,但对于类ClassB其并不需要实现方法b();(或是说,对于类ClassB来说,方法b()不是必须的。即,类ClassB既不想实现方法b(),又不想让自己成为抽象类);
这个时候,只需在接口InterA中,将方法b()设置为默认方法即可:其中需要用到default关键字
且:默认方法必须带有方法体:
注:(1)调用默认方法时,可以:使用接口引用指向实现类实例对象的引用去调用;也可以实现类变量指向实现类实例对象的变量去调用。
(2)实现类中,可以重写默认方法,也可以不重写默认方法
注:默认方法,是一种在接口严格规则下的“宽容”,一种对实现接口的类的宽容,增加了设计灵活性。(有点像,往抽象类方向退化了,但这又只是接口的特性)
2.接口中的静态方法
自然接口中的静态方法无法在实现类中重写;可以通过“接口名.”的方式调用接口中的静态方法。
注:(1)目前可以这样理解,接口本身就是由类演化过来的,虽然接口不能实例化,但接口也可以有自己的具体的功能代码(即可以有具有方法体的方法)。这儿相当于是对接口的一种补偿,为了满足某些需求,接口牺牲了太多,而给接口安排一个可以有方法体的静态方法,算是对接口损失的一种补偿
(2) 经过实测发现,实现类和实现类对象,接口引用指向实现类对象的引用,均不能访问接口中定义的静态方法。。。这一点也体现了,这个对接口的“补偿”也是有度的,而不过于随意。
3.接口应用中的常见问题
(1)在一个Java文件可以存在多个类、多个接口,但是只能存在一个public修饰的类或接口,且此Java文件名需要与public修饰的类或接口同名:
否则会报错。
(2)
(3)
下图中的:“实现类无法直接应用接口中的静态方法”具体实测结果,见文末的Comments。
Comments:
(1)经过实测:
(一)对于普通的Java类:
父类和父类对象都可以调用父类的静态方法、静态变量; 子类和子类对象都可以直接调用父类的静态方法、静态变量; 父类引用指向子类实例对象,也可以调用父类的静态方法、静态变量。
(二)对于接口及其实现类:
接口可以直接调用接口中的静态方法、静态变量; 实现类和实现类对象只能获取接口中的静态变量,不能访问调用接口的静态方法; 接口引用指向实现类对象,只能获取接口中的静态变量,不能访问调用接口的静态方法。
这儿要注意,以上两种情况的区别。
Java中一个类可以实现多个接口:
1.多接口中重名默认方法处理的解决方案
比如,在INet接口和IPhoto接口中,均有默认方法 connection;此时,SmartWatch类实现了这两个接口,如果对此不进行处理会报错:
解决方案:
在SmartWatch类中,定义一个自己的connection方法:
这个时候:三种情况下的调用,均是调用的实现类中定义的默认方法。
注:这儿相当于是对两个接口中重名方法的覆盖和摒弃
2.父类和多接口中重名方法的处理方案
另一种情况:类B继承了类A,实现了接口C和D;且类A,接口C和接口D中均有相同前面的connection方法,那么此时应该如何处理?如下:
两个接口和一个父类中,均有b方法;
首先,当实现类里没有b方法时 :没有报错
其结果:直接调用的父类中的b方法; (很显然,子类继承了父类的b方法)
注:这儿体现了,“父类和子类的关系” 比 “接口和实现类的关系” 更“铁”?
其次,当实现类里有b方法时 :自然也没有报错
其结果:就是调用的实现类里的b方法 (子类重写了父类的b方法)
1.多接口中重名常量处理的解决方案
因此解决方案如下:
2.父类和多接口中重名变量的解决方案
发现,此时“重名变量”的情况下报错了(这和重名方法的情况是不同的,父类和多接口的重名方法情况下,子类没有该方法的实现时,没有报错)。。。 这儿说明,在变量这种情况下“父类和子类的关系” 并没有比 “接口和实现类的关系” 更 “铁”。
解决方案:在子类中自己定义一个同名变量
注:类似于多接口重名的方法和变量,父类和多借口重名方法和变量的情况,根据以往开发经验,并不常遇到。
1.接口的继承
接口继承使用 extends 关键字:
一个类实现“子接口”:该实现类,需要既实现“子接口”中的方法,又要实现“父接口”中的方法。
Java中的类是单继承的,即一个子类最多只能有一个父类。那么接口呐?
Java中,接口是可以继承多个接口的
自然,如果一个类实现了接口ISon,那么该来需要实现接口,ISon,IFather,IFather2中的方法。
Java的接口多继承时:如果IFather和IFaher2两个父接口中同时存在同名的默认方法,connection():
如果“子接口”不做任何处理,则会报错:
此时解决方案:“子接口”中自己写一个同名的默认方法
2.接口中默认方法的重写和覆盖
(20200914加:接上)经过实测发现:经过处理后,一个实现了ISon接口的类,该类没有重写connection()方法,也不会报错,因为接口ISon已经处理了可能产生的冲突。
然后,如果实现类没有重写connection方法,
其:无论是谁的引用,只要是指向的实现类对象,均会调用ISon(即“子接口”)中的connection方法。(即子接口覆盖掉了两个父接口的connection方法,并将该方法传递给了实现类,)
如果,实现类中重写了connection方法:则又全都调用实现类中的connection方法了(实现类不接受其接口给予的“宽容”,依旧重写了默认方法)
通过以上两个实例可以发现:接口中的默认方法,本身这个东西的目的是“对接口实现类的宽容”,本身是更倾向于不需要实现类重写的。。。可一旦实现类(或“子接口”)重写了该默认方法,那么就会对其(上层)接口中的默认方法形成一种全面覆盖。
能多少感受到默认方法的性质吧。(其实和一般的父类子类没有太大区别。如,父类引用指向之类对象,可以调用父类中的方法和子类重写父类的方法。这儿子类重写的父类的方法,和这儿是相通的。因为“子类对像”和“实现类对象”是相通的)
对于类来说,可知:子类不会继承父类的静态方法和静态属性。 但子类对象可以访问父类的静态方法和静态属性;当在子类中添加同名的方法和属性时,这会导致(看起来)覆盖掉可访问的父类的属性和方法;
如下两个例子:
可见,子类可以访问到父类的静态方法和静态属性(仅仅是可以访问到)
当子类中写同名的方法和属性时:自然调用的就是子类中的方法和属性
又如:
这儿看看就行,暂时没有深究的需求。可看下:https://www.cnblogs.com/coder-who/p/8419184.html这个文章。
下篇文章,转载。
Java多态八的comments部分做了实测结论:
Comments:
(1)经过实测:
(一)对于普通的Java类:
父类和父类对象都可以调用父类的静态方法、静态变量; 子类和子类对象都可以直接调用父类的静态方法、静态变量; 父类引用指向子类实例对象,也可以调用父类的静态方法、静态变量。
(二)对于接口及其实现类:
接口可以直接调用接口中的静态方法、静态变量; 实现类和实现类对象只能获取接口中的静态变量,不能访问调用接口的静态方法; 接口引用指向实现类对象,只能获取接口中的静态变量,不能访问调用接口的静态方法。
这儿要注意,以上两种情况的区别。
1.问题:“子接口”能继承“父接口”的所有成员吗?
(1)如果是单继承,即“子接口”只有一个“父接口”,则可以继承“父接口”中定义的常量、抽象方法、默认方法,无法继承静态方法。
(2)如果是多继承,即“子接口”有多个“父接口”,则可以继承“父接口”中定义的抽象方;同名的常量和默认方法则无法分辨应用哪一个(报错)。
即:这儿和上面的comments结合来看,接口的静态方法本身本身就无法被其子接口和实现类访问。又可以印证, 静态方法这种对接口的补偿,仅仅是有限范围的补偿,否则,这种补偿就成了打破接口设计初衷的、泛滥式的放纵了。
(前几个博客中也体现了这些性质,这儿相当与一个总结)
2.接口和抽象类的比较
有点糊涂,看看这篇哲学文章;接口和抽象类有什么区别
语法列举:
特点:
注:上说“抽象类中静态成员和方法可以被子类继承使用”似乎不太严谨
应用:
注:以下的实例,暂且看一看,后续可能删除。
以上例子:可以发现接口中的静态常量,并没有像默认方法那样有覆盖的性质。(这和一般类中的变量是一样的。说白了,还是属性和方法的区别)
然后,在看下这个:
内部类
1.内部类定义
(1)需要借助于外部类去获取内部类的信息;
(2)内部类提供了一种更好的封装手段,可以将内部类的信息隐藏在外部类的内部,而不允许其他类随意访问;
(3)Java内部类主要分为:成员内部类、静态内部类、方法内部类、匿名内部类。
2.成员内部类
第一:内部类的定义和访问
内部类定义示例:
获取内部类对象实例的三种方法:(需要借助外部类对象) 这儿是核心重点
成员内部类重要方式:Person.Heart myHeart = new Person().new Heart();
第二:内部类的访问修饰符:
(1)内部类的访问修饰可以为 private、protected、public、或者不加访问修饰符;无论修饰符是什么均不会报错,但其内部类的可访问范围会不同;
(2)内部类前不加访问修饰符时,内部类访问权限为同一个包;跨包无法访问;
注:20200923 内部类的访问范围应该根据实际业务而决定
第三:在内部类中可以直接访问外部类的成员信息(成员属性和成员方法);
注:本部分实例篇幅很长,但内容很简单。 20200923
假设,在内部类中有个和外部类同名的成员,会访问哪一个?
其结果为:如果出现同名的属性,会优先访问内部类中定义的属性,即就近
但,如果在内部类中有和外部类同名的属性时,仍然想访问外部类的属性,怎么操作?
使用【 外部类.this.成员 】的方式,在内部类中访问外部类同名的信息(成员方法一模一样)
第四:内部类编译后的字节码class文件的形式
第五:在外部类中访问内部类的信息,必须通过内部类的实例对象才能访问内部类的成员
注:(1)20200921 可以发现,成员内部类相当于是外部类的一个普通成员(和成员属性和成员方法一样),成员属性和成员方法需要通过类的实例对象去调用,与此类似,获取成员内部类有三种方法,这三种方法也是借助于外部类对象去获取内部类的。
1.静态内部类简介
(1)使用static修饰的内部类;
(2)和静态属性、静态方法类似;静态内部类是属于类的;
(3)静态内部类对象可以直接创建,而可以不依赖于外部类对象;
2.静态内部类中访问外部类的非静态成员和静态成员
在静态内部类中,不能直接调用外部类中的非静态成员(非静态成员包括非静态属性和非静态方法,和以前了解到的同理,非静态成员是隶属于对象的,而静态成员是隶属于类的,不能“预支”)
1)如果,想在静态内部类中,调用外部类中的非静态成员,则需要,通过外部类对象去调用:
继续,发现上面获取外部类的age属性也报错了,因为【static】和【this】是冲突的,说白了还是不能在隶属于类的静态模块中,不能出现代表对象的this:继续改成这样就OK了:
2)访问外部类的静态成员:
3.获取静态内部类对象的方法
获取静态内部类对象:注意下面是 【new Person.HeartTwo()】,而不是【new Person().HeartTwo()】,即获取静态内部类对象,并没有借助于外部类对象:
4.调用静态内部类中的静态成员
如果静态内部类中有静态成员,可以通过如下方式访问:
20200923 即可以发现,直接【类.类.】的方式去调用,可能这些静态成员在类加载的时候就已经在了(建议有空可以翻翻深入理解Java虚拟机)
1.方法内部类
(1)定义在外部类方法中的内部类,也称局部内部类(命名规则和布局变量类似);
可知:1)方法中定义的局部变量只能在方法内部使用,离开方法体,就无法访问了;
2)方法内部不能定义静态成员(因为静态成员应该 直接 隶属于类);
3)方法中,不能使用public、private、protected去限定方法内成员的访问设定;( _ 注:这儿应该和对象加载和类加载的过程有关,待深入研究…或者是:方法中定义的成员仅可在方法内部访问,没必要加访问修饰符_ )
这三个对方法成员的约束,同样适用于方法内部类。
(2)方法内部类的定义 :
注意事项:
1)方法内部类中不能出现静态成员;可以定义final成员;
即使方法内部类所处的方法是静态方法也不行,(因为静态成员应该 直接 隶属于类,这儿就是直接隶属于外部类?)。
2)方法内部类中可以出现抽象成员,只是这样以后,方法内部类也得定义成抽象类,一般不这样做,只是知道可以这样即可:
对于普通类,通过 class 类名的方式定义类;然后,通过 类名 对象名 = new 构造方法();的方式获取类对象:
设想一下这种场景:在程序的逻辑中,对某个类的实例只会使用一次,而此时,这个类的名字对于整个程序就可有可无了。此时,就可以将类的定义与类的创建放到一起完成,简化程序的编写。像这种没有名字的类就成为匿名内部类。通常可以通过匿名内部类来简化对于抽象类和接口实现的操作。这篇博客主要介绍匿名内部类应用于抽象类(其实,对于非抽象类一样可以)。(其实接口也是一样的)
下面将通过实例演示匿名内部类,
1.不使用匿名内部类的情况(1这部分是废话,纯铺垫的,主要在2部分)
Person抽象父类:里面有个抽象方法read():正常情况是,弄一个抽象父类的子类,然后在子类中实现read()抽象方法,然后通过创建子类的实例对象就可以去调用子类中的实现后的read()方法。。。。从而实现最终的功能调用。 从这个过程可以发现,对于抽象父类需要明明白白的写一个子类,然后明明白白的使用子类去创建子类实例对象,然后去调用方法。这也是以前常见的策略。
Man子类,继承Person类,实现了其中的read()抽象方法:
Woman子类,继承Person类,实现了其中的read()抽象方法:
PersonTest测试类,有两种方式调用read()方法:
上面的例子非常简单,是常见的方式,没有使用匿名内部类,在调用read方法的过程中,通过两个子类获取了类的实例对象,然后通过实例对象去调用方法。
2.那么上述功能,如何通过匿名内部类的方式去实现?
首先,依旧是一个抽象类,其中有个抽象方法:目标是完成抽象方法的实现并调用实现后的方法
Person抽象父类:里面有个抽象方法read():
对此,不再写其子类了,直接在测试类采用匿名内部类的方式去实现: 匿名内部类核心部分
注:上面如果不重写read方法的错误提示如下图:看看就行
可以发现
(1)匿名内部类没有类型名称、没有实例对象名称;
(2)由于匿名内部类的特殊性,无法在匿名内部类前加访问修饰符,即private、protected、public、abstract(自然匿名内部类中不允许出现抽象方法)、static都不能加;
(3)由于匿名内部类没有名字,所以无法在匿名内部类中编写构造方法;但如果有属性想要进行初始化操作,可以使用构造代码块:
(4)匿名内部类中不能出现静态成员,静态属性和静态方法都不能出现;
(5)自然,匿名内部类可以继承父类,也可以实现接口,但不可兼得,只能是其一;
这儿说继承父类,而没有特殊强调父类必须是抽象类,实测发现, 即使是一般类(不是抽象类),匿名内部类可以同样使用 ,只是,匿名内部类中的方法就是对父类中方法的重写而已。
(6)匿名内部类对于接口也是可以的,采取同样的策略:
注:(1)匿名内部类优点:匿名内部类对内存的损耗和系统性能的影响小;
(2)匿名内部类缺点:但,由于没有类名和对象名,匿名内部类只能在局部使用一次,当下一次想重复调用时,只能重新写一遍。
(3)匿名内部类适用场景
(4)匿名内部类编译后的文件名称:
下面的PersonTest1.class两个文件,分别是两个匿名内部类编译后的文件。
即编译后的文件是【外部类$数字.class】的形式;
通过【匿名内部类】这篇博文给出的范例可知,匿名内部类可应用于这种场景:一个接口A、或一个抽象类B中定义了一个未实现的方法a(自然一个普通类C的已经实现了的方法也行,这样的话就是重写了),在其另一个类D中,需要调用这个方法a,这个时候就可以在类D的调用这个方法的地方使用匿名内部类的策略,完成对方法a的具体实现,以完成具体的功能调用。
可以发现,上面的过程并没有写出包含a方法具体实现的匿名类的名字,也没有该匿名类的实例对象(即并没有写抽象类的子类或接口的实现类),,,但通过系统浏览器可以发现,在程序编译后,有该匿名内部类的.class文件。
所以,(鉴于目前对类加载机制不熟悉的情况下)可以认为,该匿名内部类是在内存中实际存在的,是有一个匿名内部类的实体的。即也可以认为匿名内部类是一种简略的方式,一种节省的方式(如上,就省去了创建子类或实现类)。一种现用现写的方式。
也可以发现,前几节所说的成员内部类、静态内部类、方法内部类、匿名内部类均是在类中说的。其实,不仅类里面可以有内部类,接口也可以有内部类,那么对于成员内部类、静态内部类、方法内部类、匿名内部类这四个内部类在接口中的情况,是这篇博文的主要的内容。
1.接口中为什么需要内部类:
注:接口中为什么需要内部类是理解这部分的核心
2.接口中成员内部类之普通成员内部类
接口:
接口的实现类:可以发现在实现类中,接口中的普通成员内部类是可以被实现类访问的到的
实测:调用接口中的普通成员内部类中定义的show()方法
注意:(1)第一种通过接口获取接口中普通成员内部类实例对象的方法。(独立记忆即可,不要和类中的内部类获取实例对象相混淆)
(2)接口中的内部类可以import导入(这点表明,接口中的普通成员内部类可以在不借助于接口的情况下被外部访问;而对于类中的内部类来说,必须接口外部类才能访问内部类)
3.接口中成员内部类之抽象成员内部类
接口:
实现类:发现在实现类中定义了一个成员内部类AbDemo来继承接口中的抽象成员内部类AbInnerClass,然后在该子类中实现了抽象成员内部类中的抽象方法abInfo()。
调用接口中抽象成员内部类方法的策略:
(1)第一种策略:抽象成员内部类的本来应该有一个实现类,这儿把这个实现类匿名了,(即在略微不同的场景中使用了匿名内部类,然后把采用了匿名内部类策略处理后的抽象类复赋值给了abInner,注意这种“新的”用法吧)( ** 这种策略目前在说服自己理解上还存在模糊** )
(2)第二种策略:单纯是在接口的实现类中,采用成员内部类的方法,使实现类中的成员内部类继承接口中的抽象成员内部类,从而实现抽象成员内部类的抽象方法,完成功能的调用。
四个内部类的总结
0.内部类概述
1.成员内部类
2.方法内部类
3.静态内部类
4.匿名内部类
内部类常见问题:
(1)
因为这样做的话,会显得不合理
(2)方法签名
Java枚举
1.枚举的定义
为什么使用枚举:
如果在A类中定义了一个枚举,怎么在B类中访问A中定义的枚举:
2.枚举在if结构中的应用
注意:下面判定是否相等采用的是“==”;
3.枚举在switch结构中的应用
switch表达式的值可以是基本数据类型的byte、short、int、char,以及String类型, 不能是浮点数;
表达式也可以是枚举类型;
JavaUML
类的横向关系
1.依赖关系
(1)A 类中用到了B 类对象(或可描述为:A 类用到了B 类中定义的方法尤为常见);
(2)建议单向依赖,尽量避免双向依赖,防止耦合性过大;
(1)注意画图方式,虚线箭头。
由下图(摘自《Java面向对象编程》,作者:孙卫琴)描述可知:依赖关系中,B类对象不是A类的一个成员属性。
2.关联关系
(1)一个类知道另一个类的属性和方法,常见的实现方式是,B类对象是A类的成员属性;(逻辑上有需要知道的另一个类的属性和方法的需求和自然逻辑性)
(2)可以是单向关联,也可以是双向关联;
(3)注意画图方式。
注:关联可能偏向于类的课访问性
注:关联关系,掌握的不好,
3.聚合关系
(1)进化版关联关系,整体与部分的关系;
(2)注意画图方式。
可知,关联关系是平级类之间的关系,聚合关系是不平级类之间额关系。
4.组合关系
(1)关联关系的一种;
(2)一个类是另一个类的”附庸“,二者不可分。
从下图(摘自《Java面向对象编程》,作者:孙卫琴)描述可知:组合关系中,【代表部分的对象】需要依附于【代表整体的对象】,且【代表整体的对象】可以把【代表部分的对象】的管理权移交给另一个【代表整体的对象】;
【代表整体的对象】负责将【代表部分的对象】湮灭。
注:(1)这四种关系的逻辑应用场景,需要在实际业务中逐渐加深理解;
(2)通过UML 工具,可以方便梳理程序类的逻辑关系,一个辅助工具。
类的纵向关系
1.泛化关系
(1)父类到子类,一般到特殊;
(2)注意其UML中的画图方式。
2.实现关系
(1)接口(抽象类)到实现类(子类),”范例“到具体实现;
(2)注意其画图方式,虚线。