第10章-内部类II
Think in java 读书笔记
pikzas
2019.05.05
第十章 内部类
知识点
1.什么是内部类
可以将一个类定义在另一个类的内部
class OuterClass{
class InnerClass{
}
}
2.内部类的主要特征是什么
内部类的主要特征是内部类可以随意访问外部类的属性和方法,不论其访问修饰符是什么
public interface Selector {
boolean end();
Object current();
void next();
}
public class Seq {
private Object[] items;
private int next = 0;
public Seq(int size){
items = new Object[size];
}
public void add(Object item){
if(next < items.length){
items[next++] = item;
}
}
private class SeqSelector implements Selector{
private int i = 0;
@Override
public boolean end() {
return i == items.length;
}
@Override
public Object current() {
return items[i];
}
@Override
public void next() {
if(i<items.length){
i++;
}
}
}
public Selector getSelector(){
return new SeqSelector();
}
public static void main(String[] args) {
Seq sq = new Seq(10);
for (int i =0 ; i < sq.items.length ; i++){
sq.add(Integer.toString(i));
}
Selector selector = sq.getSelector(); //此时,外部类对象的引用会被传递给内部类对象
while (!selector.end()){
System.out.println(selector.current());
selector.next();
}
}
}
3.适用于内部类的一些特殊语法
.new 由外部类对象新创建内部类对象
class Outer{
class Inner{}
}
class Demo{
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
}
}
.this 在内部类如果想要获取外部类对象的引用
class DotThis{
void f() {System.out.println("DotThis.f()");}
class Inner{
public DotThis getOuterObj(){
return DotThis.this;
}
}
public Inner getInner(){
return new Inner();
}
public static void main(String[] args){
DotThis dotThis = new DotThis();
DotThis.Inner inner = dotThis.getInner();
System.out.println(inner.getOuterObj().equals(dotThis)); // 输出为true
inner.getOuterObj().f(); // inner.getOuterObj() 等同于 dotThis
}
}
.new 语法也可已看出,必须要先有外部类实例,再由这个实例.new 出内部类。(静态内部类除外)
4.内部类与接口的配合使用
内部类实现接口,并将内部类访问限定为private或者protected,从而将实现隐藏在该内部类中。
public interface Dest {
int value();
}
public class Demo {
private class DestImp implements Dest{
@Override
public int value() {
return 0;
}
}
public Dest getDest(){
return new DestImp();
}
}
public class Test {
public static void main(String[] args) {
Demo demo = new Demo();
Dest dest = demo.getDest();
dest.value();
}
}
5.局部内部类
以上看到的内部类都是直接定义在外部类的作用域内,如果让一个内部类定义在外部类的某个方法内部时,这就称作局部内部类。
并且当作用域超出了这个方法的时候,外部是访问不到该类的。
public interface Fruit {
String desc();
}
public class Test {
public Fruit getFruit(){
class Apple implements Fruit{
@Override
public String desc() {
return "i am apple";
}
}
return new Apple();
}
// getFruit方法之外,是访问不到Apple类的
// private Fruit fruit = new Apple();
}
6.匿名内部类
上面的局部内部类将Apple的定义和new 创建新对象的语法放在一起就是匿名内部类。如果返回的是具体的类,看起来也就没什么特殊了。
public interface Fruit {
String desc();
}
public class Test2 {
public Fruit getFruit(){
return new Fruit(){
@Override
public String desc() {
return "i am apple";
}
};
}
public static void main(String[] args) {
Test2 test2 = new Test2();
Fruit fruit = test2.getFruit();
System.out.println(fruit.desc());
}
}
6.1.匿名内部类可以是具体类,甚至可以对其方法进行override
public class Wrapping {
private int i;
public Wrapping(int x){
this.i = x;
}
public int value(){
return i;
}
}
public class Outer {
public Wrapping getItem(int x){
return new Wrapping(x){
@Override
public int value(){
return super.value() * 47;
}
};
}
public Wrapping getItem2(int x){
return new Wrapping(x);
}
public static void main(String[] args) {
Outer outer = new Outer();
Wrapping wrapping = outer.getItem(10);
Wrapping wrapping2 = outer.getItem2(10);
System.out.println(wrapping.value());
System.out.println(wrapping2.value());
}
}
------输出结果------
470
10
6.2.对匿名内部类的字段进行初始化
- 使用外部类传入的数据进行初始化,此时该数据必须为final的
public interface SampleInterface {
int value();
}
public class InitDemo {
public SampleInterface getSample(final int x){ //此处final必须为final的 可以理解为外部对象指向的数据不能变动,否则内部类对象会很困惑。
return new SampleInterface() {
private int i = x;
@Override
public int value() {
return i;
}
};
}
}
- 通过初始化代码块完成类似于构造器的功能
public abstract class Base {
public Base(int i){
System.out.println("base constructor, i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i){ //此处的变量不用是final的,因为内部类并没用到i
return new Base(i){
{
System.out.println("inside instance"); // 通过静态代码块可以实现初始化
}
public void f(){
System.out.println("in inner f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}
从上面对于传入内部类的参数是否要加上final的修饰规定是,如果内部类用到了外部传入的那个变量,则需要为final的。
内部类可以拿来扩展类,也可以拿来实现接口,但是不能两个都做到,也只能一次实现一个接口。
7.工厂方法的内部类实现方式
public interface Game {
boolean move();
}
public interface GameFactory {
Game getGame();
}
public class Chess implements Game {
private Chess(){};
private int moves = 0;
private static final int MOVES = 4;
@Override
public boolean move() {
System.out.println("Chess moves" + moves);
return ++moves != MOVES;
}
public static GameFactory factory = new GameFactory(){
public Game getGame(){
return new Chess();
}
};
}
public class Checkers implements Game {
private Checkers(){}
private int moves = 0;
private static final int MOVES = 3;
@Override
public boolean move() {
System.out.println("Checker moves" + moves);
return ++moves != MOVES;
}
public static GameFactory factory = new GameFactory() {
@Override
public Game getGame() {
return new Checkers();
}
};
}
public class Test {
public static void playGame(GameFactory factory) {
Game game = factory.getGame();
while(game.move());
}
public static void main(String[] args) {
playGame(Chess.factory);
playGame(Checkers.factory);
}
}
-------输出结果----
Chess moves0
Chess moves1
Chess moves2
Chess moves3
Checker moves0
Checker moves1
Checker moves2
8.嵌套类(也成为静态内部类)
前面讲到的内部类的创建都依赖于外部类的对象,如果内部类定义的时候是static的,那么外部对象就不需要了。
同时因为内部类是static的,那么如果想要使用外部类中的属性或者方法,那么那些属性或者方法必须也是static的。
public class Outer {
private int x = 123;
private static int y = 999;
public static class OneImpl implements OneInterface{
public void f(){
// System.out.println("inner class" + x); 此处因为x不是static的,会提示编译错误。
System.out.println("inner class " + y);
}
}
public static void main(String[] args) {
OneInterface one = new OneImpl();
one.f();
}
}
8.1.嵌套类的应用
由于接口中的属性和方法默认都是public static的,所以其中可以来嵌套一些需要子类实现都通用的方法。
public interface DemoInterface {
void fun();
class DemoImpl implements DemoInterface{
public void fun(){
System.out.println("可以实现我自己的外部接口");
}
}
}
public class DemoTest implements DemoInterface {
@Override
public void fun() {
System.out.println("DemoTest");
}
public static void main(String[] args) {
DemoInterface demo = new DemoTest();
DemoInterface demo2 = new DemoImpl();
demo.fun();
demo2.fun();
}
}
-----输出结果-----
DemoTest
可以实现我自己的外部接口
嵌套类不同于普通的内部类的另一点在于,编译生成的.class文件名称都是形如Outer$Inner.class这个格式。
而且嵌套类内部还能有嵌套类,以及static声明的属性及方法。
9.内部类无论内部有多少层,内一层的类都可以无条件的访问外部类
10.内部类存在的意义
内部类存在的主要目的是为了解决java没有多继承导致的一些麻烦
- java可以很好的解决多接口的实现问题,如下例子
public interface A {
}
public interface B {
}
public class X implements A,B {
}
public class Y implements A {
B getB(){
return new B() {};
}
}
public class TestOne {
public static void funA(A a) {
}
public static void funB(B b) {
}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
funA(x);
funA(y);
funB(x);
funB(y.getB());
}
}
- 但是对于要求子类同时实现抽象类和父类问题时,只有内部类可以派上用场
public class M {
}
public abstract class N {
}
public class Z extends M {
N getN(){
return new N() {};
}
}
public class TestTwo {
public static void funM(M m) {
}
public static void funN(N n) {
}
public static void main(String[] args) {
Z z = new Z();
funM(z);
funN(z.getN());
}
}
11.内部类的继承
内部类与外部类紧紧联系在一起,那么如果想要继承一个外部类的内部类,就需要使用到特殊的语法 OutClass.super();
public class DemoOuter {
class Inner{}
}
public class ExtInner extends DemoOuter.Inner {
public ExtInner(DemoOuter demoOuter){
demoOuter.super(); //必须添加如此这般的构造器并显示指定父类对象引用
}
}
12.内部类初始化顺序
内部类的构造器会先调用,然后才会调用外部类构造器。
内部类不存在覆盖问题,Egg中的Yolk和BigEgg中的Yolk完全是两个不同的类。
public class Egg {
class Yolk{
public Yolk(){
System.out.println("Egg York"); // 1 3
}
public void f(){
System.out.println("Egg f()");
}
}
private Yolk york = new Yolk();
public Egg(){
System.out.println("Egg"); // 2
}
public void insert(Yolk y){
this.york = y;
}
public void g(){
york.f();
}
}
public class BigEgg extends Egg {
class Yolk extends Egg.Yolk{
public Yolk(){
System.out.println("BigEgg Yolk"); // 4
}
public void f(){
System.out.println("BigEgg f()"); // 5
}
}
public BigEgg(){
insert(new Yolk());
}
public static void main(String[] args) {
BigEgg egg = new BigEgg(); // 0
egg.g();
}
}
-----输出结果-----
Egg York
Egg
Egg York
BigEgg Yolk
BigEgg f()
13.局部内部类和匿名内部类的区别
前面讲过,将显示的类声明写出来,在返回一个对象的方式叫做局部内部类,但是如果直接new 接口并返回,那么他就是匿名内部类。
如果某个场景下,需要返回一个接口的多种不同实现,那么只有局部内部类做得到,匿名内部类是做不到的。
14.内部类标识符
匿名内部类 --> LocalInnerClass$1.class
局部内部类 --> LocalInnerClass$Inner.class
第10章-内部类II的更多相关文章
- Linux就这个范儿 第10章 生死与共的兄弟
Linux就这个范儿 第10章 生死与共的兄弟 就说Linux系统的开机.必须经过加载BIOS.读取MBR.Boot Loader.加载内核.启动init进程并确定运行等级.执行初始化脚本.启动内核模 ...
- 20190821 On Java8 第十一章 内部类
第十一章 内部类 一个定义在另一个类中的类,叫作内部类. 链接外部类 内部类是一种名字隐藏和组织代码的模式. 内部类拥有其外围类的所有元素的访问权. 内部类 .this 和 .new的使用 this: ...
- 《构建之法》之第8、9、10章读后感 ,以及sprint总结
第8章: 主要介绍了软件需求的类型.利益相关者,获取用户需求分析的常用方法与步骤.竞争性需求分析的框架NABCD,四象限方法以及项目计划和估计的技术. 1.软件需求:人们为了解决现实社会和生活中的各种 ...
- 敏捷软件开发:原则、模式与实践——第10章 LSP:Liskov替换原则
第10章 LSP:Liskov替换原则 Liskov替换原则:子类型(subtype)必须能够替换掉它们的基类型(base type). 10.1 违反LSP的情形 10.1.1 简单例子 对L ...
- 孙鑫视频学习:对第10章设置线宽时为什么不调用UpDateData(TRUE)的理解
在第10章10.2.1小节中,首先分别对视图类和对话框类添加了一个名为m_nLineWidth的int型变量,再将用户在CSetting dlg对话框的edit控件中输入的线宽值记录在dlg.m_nL ...
- 第10章 系统级I/O
第10章 系统级I/O 10.1 Unix I/O 一个Unix文件就是一个m个字节的序列:B0,B1,…,BK,…,Bm-1 Unix I/O:一种将设备优雅地映射为文件的方式,允许Unix内核引出 ...
- 高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化
高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化 作为一名Linux系统管理员,最主要的工作是优化系统配置,使应用在系统上以最优的状态运行.但硬件问题.软件问题.网络环境等 ...
- 【翻译】《深入解析windows操作系统第6版下册》第10章:内存管理
[翻译]<深入解析windows操作系统第6版下册>第10章:内存管理(第一部分) [翻译]<深入解析windows操作系统第6版下册>第10章:内存管理(第二部分) [翻译] ...
- 《构建之法》第8、9、10章读后感和Sprint总结
<构建之法>第8.9.10章读后感 第八章重点讲了需求分析,在一个项目中,需求分析是最基础也是最重要的,只有充分了解了用户需求,我们才不会走弯路,才能做出正确的规划,保证项目的进行是按照 ...
随机推荐
- SpringBoot整合WEB开发--(六)CROS支持
简介: CROS(Cross-Origin Resource Sharing)是由W3C制定的一种跨域资源共享技术标准,其目的为了解决前端的跨域请求,在JavaEE开发中,最常见的前端跨域请求解决方案 ...
- ASP.NET MVC入门到精通——MVC请求管道
https://www.cnblogs.com/jiekzou/p/4896315.html 本系列目录:ASP.NET MVC4入门到精通系列目录汇总 ASP.NET MVC的请求管道和ASP.NE ...
- mysql对表中数据根据某一字段去重
要删除重复的记录,就要先查出重复的记录,这个很容易做到 注意:这是查出所有重复记录的第一条记录,需要保留,因此需要添加查询条件,查出所有的重复记录 ) ) 然后 delete from cqssc w ...
- 使用Vue创建一个新项目
1.环境 保证已经安装好了node\npm\vue等工具,将路径设置为想要建立新项目的文件夹路径 2.关于npm与cnpm npm包管理器,是集成在node中的,node环境安装完成,npm包管理器也 ...
- linux - mysql:注意事项
1.mysql和orcal数据中的数据类型不一致,比如:mysql中没有类型varchar2.number 2.mysql环境中的命令后面都带一个分号作为命令结束符 - “:”
- 294. 翻转游戏 II
题目: 链接:https://leetcode-cn.com/problems/flip-game-ii/ 你和朋友玩一个叫做「翻转游戏」的游戏,游戏规则:给定一个只有 + 和 - 的字符串.你和朋友 ...
- 优酷1080p的kux格式文件转码
@echo off for /r . %%i in (*.kux) do ( "C:\Program Files (x86)\YouKu\YoukuClient\nplayer\ffmpeg ...
- 月薪20k的web前端开发程序员,他们都会的这6招
web前端工程师是近几年的新兴职业,也是目前火爆而且高薪的职业.不同的公司也有不同的叫法,比如:网页界面开发,网站设计等,要学好web前端开发,需要掌握什么方法与技巧? 一.div和table 这个是 ...
- UI组件的学习
今天继续学习UI的组件知识,包括文本框,编辑框,普通按钮,图片按钮,单选按钮以及复选框组件,今天所学的组件的方法及属性与之前的组件大部分相同. 1. 文本框组件 TextView 文本框组件就是最常见 ...
- Flink流处理(一)- 状态流处理简介
1. Flink 简介 Flink 是一个分布式流处理器,提供直观且易于使用的API,以供实现有状态的流处理应用.它能够以fault-tolerant的方式高效地运行在大规模系统中. 流处理技术在当今 ...