目录
第一节 静态内部类和非静态内部类的比较
1.1 静态内部类和非静态内部类的区别
静态内部类只能访问外部类的静态成员和静态方法
非静态内部类不管是静态方法还是非静态方法都可以在非静态内部类中方为
静态内部类和非静态内部类主要的不同:
(1)静态内部类不依赖于外部类的实例而被实例化,是在类初始化时就被加载的。但是非静态内部类需要外部类被实例化后才会被实例化。
(2)静态内部类不需要持有外部类的引用,但非静态内部类需要持有对外部类的引用。(如果真的需要上下文环境建议使用Application的Context,可以一定程度少减少内存泄漏。比如Handler,ViewHolder等)
(3)静态内部类不能访问外部类的非静态成员变量和非静态方法,它只能访问外部类的静态成员变量和静态方法,非静态内部类能够访问外部类的静态和非静态的成员和方法
1.2 内部类有哪些?
内部类主要有四种:静态内部类,非静态内部类,局部内部类和匿名内部类
1.3 局部内部类
在外部类的方法中定义的类叫做局部内部类,其中用的方为是所在的方法内。它不能被private、public、protected来修饰,它只能访问方法中定义的final类型的局部变量。
1.4 匿名内部类
在匿名内部类是一种没有类名的内部类
(1)匿名内部类一定在 new 的后面,匿名内部类必须继承一个父类或者实现一个接口。
(2)匿名内部类不能有构造函数。
(3)只能创建匿名内部类的一个实例。
(4)在 Java8 之前,如果匿名内部类需要访问外部类的局部变量,则必须用final修饰外部类的局部变量,Java8 之后取消了这个限制。
第二节 多态的理解和应用
2.1多态概述
1. 多态是继封装、继承之后,面向对象的第三大特性。
2. 多态现实意义理解:
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个 具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
3.多态体现为父类引用变量可以指向子类对象。
4.前提条件:必须有子父类关系。
注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
5.多态的定义与使用格式
定义格式:父类类型 变量名=new 子类类型();
6.理解:
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
2.2多态中成员的特点
1.多态成员变量:编译运行看左边
Father father=new Son();
System.out.println(father.num);//father是父中的值,只能取到父中的值
2.多态成员方法:编译看左边,运行看右边
Father father=new Son();
//father的门面类型是Fu,但实际类型是Zi, 所以调用的是重写后的方法。
System.out.println(father.show());
2.3 instanceof关键字
作用:用来判断某个对象是否属于某种数据类型。
注意: 返回类型为布尔类型
Father f1=new Father();
Father f2=new Son();
if(f1 instanceof Father){
System.out.println("f1是Father的类型");
}
if(f2 instanceof Son){
System.out.println("f1是Son的类型");
}
2.4多态的转型
多态的转型分为向上转型和向下转型两种
向上转型:多态本身就是向上转型过的过程 使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型 使用格式:子类类型 变量名=(子类类型) 父类类型的变量;
适用场景:当要使用子类特有功能时。
2.5多态案例:
例1:(理解多态,可以重点看这个案例)
public class App {
public static void main(String[] ars) {
People p = new Stu();
p.eat(); //声明父类new子类 实质是调用子类,调用复写方法也是一样
//调用特有的方法
Stu s = (Stu) p;
s.study();//类型强转调用父类方法,若调用非复写方法,还是调用子类
}
}
class People {
public void eat() {
System.out.println("吃饭");
}
}
class Stu extends People {
@Override
public void eat() {
System.out.println("吃水煮肉片");
}
public void study() {
System.out.println("好好学习");
}
}
class Teachers extends People{
@Override
public void eat(){
System.out.println("吃樱桃");
}
public void teach(){
System.out.println("认真授课");
}
}
例2:请问题目运行结果是什么?
public class App {
public static void main(String[] args) {
A a = new A();
a.show();
B b = new B();
b.show();
}
}
class A {
public void show() {
show2();
}
public void show2() {
System.out.println("A");
}
}
class B extends A {
public void show2() {
System.out.println("B");
}
}
class C extends B {
public void show() {
super.show();
}
public void show2() {
System.out.println("C");
}
}
第三节 java方法的多态性理解
3.1 什么是java的多态
主要区别在于是否把方法的重写算做多态。
a. 编译时多态:方法的重载;
b. 运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称
为运行时多态。(我们平时说得多的事运行时多态,所以多态主要也是指运行时多态);
上述描述认为重载也是多态的一种表现,不过多态主要指运行时多态。
3.2 运行时多态
a. 面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
b. 多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
c. 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
d. 多态的作用:消除类型之间的耦合关系
e. 现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。
多态存在的三个必要条件 一:
- 要有继承;
- 要有重写
- 父类引用指向子类对象。
多态的好处:
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代 具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用 效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤 其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
第四节 Java中接口和继承的区别
实际概念别:
1:不同的修饰符修饰(interface),(extends)
2:在面向对象编程中可以有多继承!但是只支持接口的多继承,不支持'继承'的多继承,而继承在java中具有单根性,子类只能继承一个父类 3:在接口中只能定义全局常量,和抽象方法而在继承中可以定义属性方法,变量,常量等... 4:某个接口被类实现时,在类中一定要实现接口中的抽象方法而继承想调用那个方法就调用那个方法,毫无压力
接口是:对功能的描述
继承是:什么是一种什么
始终记着:你可以有多个干爹(接口),但只能有一个亲爹( 继承)
举例:如果狗的主人只是希望狗能爬比较低的树,但是不希望它继承尾巴可以倒挂在树上,像猴子那样可以飞檐走壁,以免主人管不住它。
那么狗的主人肯定不会要一只猴子继承的狗。
设计模式更多的强调面向接口。猴子有两个接口,一个是爬树,一个是尾巴倒挂。我现在只需要我的狗爬树,但是不要它尾巴倒挂,那么我只要我的狗实现爬树的接口就行了。同时不会带来像继承猴子来带来的尾巴倒挂的副作用。这就是接口的好处。
第五节 线程池的好处
5.1线程池的好处
线程池是啥子,干啥使它呀,老子线程使得好好的,非得多次一举,哈哈,想必来这里看这篇文章的都对线程池有点了解。那么我来整理整理线程池的好处吧。
5.1.1线程池的重用
线程的创建和销毁的开销是巨大的,而通过线程池的重用大大减少了这些不必要的开销,当然既然少了这么多消费内存的开销,其线程执行速度也是突飞猛进的提升。
5.1.2控制线程池的并发数
初学新手可能对并发这个词语比较陌生,特此我也是结合百度百科和必生所学得出最优解释,万万记着并发可跟并行不一样。
并发:在某个时间段内,多个程序都处在执行和执行完毕之间;但在一个时间点上只有一个程序在运行。头脑风暴:老鹰妈妈喂小雏鹰食物,小雏鹰很多,而老鹰只有一张嘴,她需要一个个喂过去,到最后每个小雏鹰都可以吃到,但是在一个时间点里只能有一个小雏鹰可以吃到美味的食物。
并行:在某个时间段里,每个程序按照自己独立异步的速度执行,程序之间互不干扰。头脑风暴:这就好似老鹰妈妈决定这样喂食太费劲于是为每个小雏鹰请了个保姆,这样子在一个时间点里,每个小雏鹰都可以同时吃到食物,而且互相不干扰。
回到线程池,控制线程池的并发数可以有效的避免大量的线程池争夺CPU资源而造成堵塞。头脑风暴:还是拿老鹰的例子来讲,妈妈只有一个,要这么一个个喂下去,一些饿坏的小雏鹰等不下去了就要破坏规则,抢在靠前喂食的雏鹰面前,而前面的雏鹰也不是吃软饭的,于是打起来了,场面混乱。老鹰生气了,这么不懂事,谁也别吃了,于是造成了最后谁也没食吃的局面。
5.1.3、线程池可以对线程进行管理
线程池可以提供定时、定期、单线程、并发数控制等功能。比如通过 ScheduledThreadPool线程池来执行S秒后,每隔N秒执行一次的任务。
5.2线程池的详解
想必看完上面那篇博客,大家可谓赞不绝口,不过可能有些小伙伴还是记不下来,还有些小伙伴觉得好恶心呀,怎么都是厕所啥的呀!哈哈别着急,我来给大家一种好记的办法。
先来讲讲参数最多的那个构造方法,主要是对那几个烦人的参数进行分析。
5.2.1 ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
这里是7个参数(我们在开发中用的更多的是5个参数的构造方法),OK,那
我们来看看这里七个参数的含义:
corePoolSize 线程池中核心线程的数量
maximumPoolSize 线程池中最大线程数量
keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等
workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。
threadFactory 为线程池提供创建新线 的功能,这个我们一般使用默 认即可
handler 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。
emmmmm....看到那么多烦人的概念,是不是有点头大了,我反正是头 大了。
这7个参数中,平常最多用到的是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue.在这里我主抽出corePoolSize、maximumPoolSize和workQueue三个参数进行详解。
maximumPoolSize(最大线程数) = corePoolSize(核心线程数) + noCorePoolSize(非核心线程数);
- 当currentSize<corePoolSize时,没什么好说的,直接启动一个核 心线程并执行任务。
- 当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。
- 当workQueue已满,但是currentSize<maximumPoolSize时,会立即开启一个非核心线程来执行任务。
- 当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。
什么currentSize,corePoolSize,maximumPoolSize,workQueue比来比去 的都比迷糊了,哈哈,那我举个烧烤店的例子来想必大家理解起来更快。
夏天了,很热,所以很多烧烤店都会在外面也布置座位,分为室内、室外两个地方可以吃烧烤。(室内有空调电视,而且室内比室外烧烤更加优惠,而且外面下着瓢泼大雨所以顾客会首先选择室内)
corePoolSize(烧烤店室内座位),cuurentPoolSize(目前到烧烤店的顾客数量),maximumPoolSize(烧烤店室内+室外+侯厅室所有座位),workQueue(烧烤店为顾客专门设置的侯厅室)
第(1)种,烧烤店人数不多的时候,室内位置很多,大家都其乐融融, 开心的坐在室内吃着烧烤,看着世界杯。
第(2)种,生意不错,室内烧烤店坐无空席,大家都不愿意去外面吃, 于是在侯厅室里呆着,侯厅室位置没坐满。
第(3)种,生意兴隆,室内、侯厅室都坐无空席,但是顾客太饿了,剩 下的人没办法只好淋着雨吃烧烤,哈哈,好可怜。
第(4)种,生意爆棚,室内、室外、侯厅室都坐无空席,在有顾客过来 直接赶走。
5.2.2其他线程池的记法
剩下的那四种主要的线程池大概思路,用法在我推荐的博客里都有详细解说,在这里我就不一一道来了,在这里主要是跟大家分享一种特别容易记住这四种线程池的方法,在大家写代码,面试时可以即使想到这四种线程池。
(1)FixedThreadPool:
Fixed中文解释为固定。结合在一起解释固定的线程池,说的更全面点就是,有固定数量线程的线程池。其 corePoolSize=maximumPoolSize,且keepAliveTime为0,适合线程稳 定的场所。
(2)SingleThreadPool:
Single中文解释为单一。结合在一起解释单一的线程池,说的更全面点就是,有固定数量线程的线程池,且数量为一,从数学的角度来看SingleThreadPool应该属于FixedThreadPool的子集。其corePoolSize=maximumPoolSize=1,且keepAliveTime为0,适合线程同步操作的场所。
(3)CachedThreadPool:
Cached中文解释为储存。结合在一起解释储存的线程池,说的更通俗易懂,既然要储存,其容量肯定是很大,所以他的corePoolSize=0,maximumPoolSize=Integer.MAX_VALUE(2^32-1一个很大的数字)
(4)ScheduledThreadPool:
Scheduled中文解释为计划。结合在一起解释计划的线程池,顾名思义既然涉及到计划,必然会涉及到时间。所以ScheduledThreadPool是一个具有定时定期执行任务功能的线程池。