小伙伴们,小编又来了,这篇文章承接上文,本节将详细讲解内部,学完本文章,面向对象编程也告一段落了,我们开始学习了。
第三节 内部类
https://www.bilibili.com/video/BV1L7411N77n/?p=110
内部类是一类特殊的类,指的是定义在一个类的内部的类。实际开发中,为了方便的使用外部类的相关属性和方法,这时候我们通常会定义一个内部类。
一般情况,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内部定义,称为内部类(innerclasses)。
在Java中内部类主要分为成员非静态成员内部类、静态成员内部类、局部内部类、匿名内部类。
3.1 非静态成员内部类
https://www.bilibili.com/video/BV1L7411N77n/?p=111
作为类的成员存在,和成员变量、成员方法、构造方法、代码块并列。因为是类的成员,所以非静态成员内部类可以使用public、protected 、默认、private修饰,而外部类只能使用public、默认修饰。
【示例11】非静态成员内部类
总结1:基本特征
内部类可以直接访问外部类的成员
外部类不能直接访问内部类的成员,需要先创建对象再通过对象名访问
内部类如何访问外部类的同名成员变量:OuterClass.this.num
必须先创建外部类的对象,才能创建内部类的对象。非静态成员内部类是属于某个外部类对象的
总结2:更多特征
非静态内部类不能有静态方法、静态属性和静态初始化块。
外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
3.2 静态成员内部类
【示例12】静态成员内部类
总结:
静态内部类只能够访问外部类的静态成员
静态内部类如何访问外部类的同名的成员变量:OuterClass.num
静态内部类属于整个外部类的。创建静态内部类的对象,不需先创建外部类的对象
外部类可以通过类名直接访问内部类的静态成员,访问非静态成员依旧需要先创建内部类对象。
3.3 局部内部类
还有一种内部类,它是定义在方法内部的,作用域只限于本方法,称为局部内部类。
局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。
局部内部类在实际开发中应用很少。
【示例13】局部内部类
注意:局部内部类访问所在方法的局部变量,要求局部变量必须使用final修饰。JDK1.8中final可以省略,但是编译后仍旧会加final。
第四节 虚拟机和垃圾回收
https://www.bilibili.com/video/BV1L7411N77n/?p=79
4.1 匿名内部类
匿名内部类就是内部类的简化写法,是一种特殊的局部内部类。
前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类。
本质是什么呢?是一个继承了该类或者实现了该接口的子类匿名对象。
适合那种只需要创建一次对象的类。比如:Java GUI编程、Android编程键盘监听操作等等。比如Java开发中的线程任务Runnble、外部比较器Comparator等。
语法:
问题:一个类实现Comparable接口,只能指定一种比较大小的规则,如果需要有更多的比较规则,怎么办?
/p>
p class="MsoNormal">Comparable:内部比较器 public class Student implements Comparable{} 内部比较器只能有一个,一般采用最经常使用的比较规则
/p>
p class="MsoNormal">Comparator 外部比较器 可指定多个 不需要Student实现该接口,而是定义专门的类。
/p>
p class="MsoNormalIndent" style="margin-left:0.0000pt;mso-para-margin-left:0.0000gd;text-indent:0.0000pt;
mso-char-indent-count:0.0000;">
br/>
br/>
span class="font-size-16">【示例14】定义Comparator接口
/span>
img src="https://i0.hdslb.com/bfs/article/a0814d3d0ebfbb8e8e4d73692747b52d51868588.png" data-src="https://i0.hdslb.com/bfs/article/a0814d3d0ebfbb8e8e4d73692747b52d51868588.png" width="610" height="381" data-size="27758"/>
figcaption class="caption" contenteditable="">
/figcaption>
strong>
/strong>
br/>
strong>
/strong>
span class="font-size-16">【示例15】实现Comparator接口的类
/span>
img src="https://i0.hdslb.com/bfs/article/064c538d6caee1964d2e1dc5726b6bd53766799c.png" data-src="https://i0.hdslb.com/bfs/article/064c538d6caee1964d2e1dc5726b6bd53766799c.png" width="610" height="759" data-size="70389"/>
figcaption class="caption" contenteditable="">
/figcaption>
p>
/p>
strong>
/strong>
br/>
span class="font-size-16">【示例16】使用匿名内部类实现外部比较器
/span>
img src="https://i0.hdslb.com/bfs/article/8659b6229dce0896e7d09cedffcafcc30eeddb78.png" data-src="https://i0.hdslb.com/bfs/article/8659b6229dce0896e7d09cedffcafcc30eeddb78.png" width="610" height="1011" data-size="99401"/>
figcaption class="caption" contenteditable="">
/figcaption>
strong>
/strong>
br/>
strong>
/strong>
span class="color-pink-03">结论:
/span>
li>
匿名内部类可以实现一个接口,也可以继承一个类(可以是抽象类)。
/li>
li>
匿名内部类只能实现一个接口,而不是多个
/li>
li>
必须实现所有的方法,匿名内部类不能是抽象类
/li>
li>
匿名内部类不可能有构造方法,因为类是匿名的
/li>
li>
匿名内部类没有访问修饰符
/li>
li>
如果想实现构造方法的一些初始化功能,可以通过代码块实现
/li>
li>
如果要访问所在方法的局部变量,该变量需要使用final修饰。(JDK1.8可省略final)
/li>
span class="font-size-16">
4.2 内部类的作用和使用场合/span>
strong>
/strong>
span class="color-pink-03">
内部类的作用:/span>
li>
内部类提供了更小的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
/li>
li>
内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
/li>
li>
接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。
/li>
li>
用匿名内部类实现回调功能。我们用通俗讲解就是说在Java中,通常就是编写一个接口,然后你来实现这个接口,然后把这个接口的一个对象作以参数的形式传到另一个程序方法中, 然后通过接口调用你的方法,匿名内部类就可以很好的展现了这一种回调功能
/li>
strong>内部类的使用场合:
/strong>
li>
由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。
/li>
li>
使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。
/li>
strong>
4.3/strong>
strong>
/strong>
strong>
虚拟机及其构成/strong>
strong>
/strong>
img src="https://i0.hdslb.com/bfs/article/83f6b07a3ae6c798ac03362bf19d38395e9e14c0.png" data-src="https://i0.hdslb.com/bfs/article/83f6b07a3ae6c798ac03362bf19d38395e9e14c0.png" width="314" height="225" data-size="66543"/>
figcaption class="caption" contenteditable="">
/figcaption>
strong>
程序计数器/strong>
strong>
Java虚拟机栈和本地方法栈/strong>
strong>
Java堆/strong>
strong>
方法区/strong>
br/>
br/>
img src="https://i0.hdslb.com/bfs/article/7b5579ccf576d3e98c875c8eeceb2face1557ddb.png" data-src="https://i0.hdslb.com/bfs/article/7b5579ccf576d3e98c875c8eeceb2face1557ddb.png" width="516" height="449" data-size="142493"/>
figcaption class="caption" contenteditable="">
/figcaption>
strong>
/strong>
strong>堆内存分为三部分
/strong>
br/>
strong>年轻代:分为eden区+两个大小相同的存活期s0、s1;
/strong>
strong>老年代
/strong>
strong>永久代
/strong>
strong>注意:
/strong>JDK6 及以前版本,字符串常量池是放在堆的Perm区的,Perm区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError:PermGen space错误。
strong>
4.4/strong>
strong>
/strong>
strong>
垃圾回收/strong>
strong>
/strong>
strong>分代垃圾回收机制
/strong>,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。我们将对象分为三种状态:年轻代、年老代、持久代。同时,将处于不同状态的对象放到堆中不同的区域。 JVM将堆内存划分为 Eden、Survivor 和 Tenured/Old 空间。
span class="font-size-12">
/span>
strong>
垃圾回收的相关技能点/strong>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">垃圾回收机制主要是回收JVM堆内存里的对象空间。
/p>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">现在的JVM有多种垃圾回收实现算法,表现各异。
/p>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
/p>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
/p>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
/p>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
/p>
p class="16" style="margin-left:36.7500pt;text-indent:-21.0000pt;mso-char-indent-count:0.0000;
mso-layout-grid-align:none;layout-grid-mode:char;mso-list:l0 level1 lfo1;">永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
/p>
【示例17】finalize()和gc()
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/25753.html