Java基础 - 内部类
2021/4/18 12:26:58
本文主要是介绍Java基础 - 内部类,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
目录- 引言
- 定义与用法
- 访问权限
- 内外通信
- .this
- .new
- 局部内部类
- 匿名内部类
- demo
- 初始化
- 静态内部类
- 接口内部类
- 内部类作用小结
- 命名
- 代码结构
- 访问控制
- "多重继承"
- 参考资料
引言
本文主要还是记录下内部类的基础语法,具体的作用等接触更多的代码之后回来小结
定义与用法
内部类,顾名思义就是在一个类的内部再定义一个类
public class Outer { private String id; public Outer(String id) { this.id = id; } /** * 内部类可以是任意的访问权限 */ public class Inner { // 可以任意的访问外部的属性 private String innerId = "inner" + id; } }
访问权限
- 内部类本身可以设置任意的访问权限(
private
,default
,protected
,public
) - 内部类可以访问外部类任意权限的属性或方法(普通内部类)
正是由于内部类可以定义为private
,所以内部类的作用之一就是可以对外屏蔽类的存在
这一点在内部类实现接口时作用会比较明显,例如说Iterator
就是一个比较典型的例子List<String> list = new ArrayList<>(); Iterator<String> iterator = list.iterator();
对于客户端来说,你拿到的是一个Iterator
,而非ArrayList
的内部类对象Itr
,你能访问的也只有Iterator
接口所提供的方法
也就是说,ArrayList
提供了迭代器的功能,但是完全对客户端屏蔽其实现 - 非静态内部类不能定义静态成员,除非它是final(并且是编译期常量)的
为什么非静态内部类中不能定义静态成员,为什么final又是可以的,参考下面这篇文章:
为什么非静态内部类中不能有static属性的变量,却可以有static final属性的变量? - 静态内部类不能访问外部类的非静态成员(就好比说静态方法不能访问非静态成员一个意思)
内外通信
这一小结主要就是说明访问外部属性以及创建内部类对象的方法
.this
普通的内部类(非静态内部类)之所以能够访问外部类的所有属性,肯定是因为有一个应用指向了外部类
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟 .this --- 《OnJava8》
- 如果直接使用this,则this指向的是内部类的对象,外部类 + .this才是指向外部类的对象
- 访问外部属性时并非都要加上
.this
例如下面的代码示例中我们使用Outer.this.getId()
,其实直接调用getId()即可
但有时这时必要的,比如说内部类和外部类的方法、属性重名了,我们在访问外部类时就必须使用 【外部类 + .this】,否则就无限循环了
public class Outer { private String id; public Outer(String id) { this.id = id; } private String getId() { return this.id; } private void setId(String id) { this.id = id; } /** * 内部类可以是任意的访问权限 */ public class Inner { // 可以任意的访问外部的属性 private String innerId = "inner" + id; public String getOutId() { return getId(); } public void setId(String id) { Outer.this.setId(id); } } }
.new
创建内部类对象时必须要使用外部类的对象进行创建
public class OuterTest { public static void main(String[] args) { Outer outer = new Outer("张三"); Outer.Inner inner = outer.new Inner(); System.out.println("inner class getId: " + inner.getId()); } }
局部内部类
有时一个内部类的对象只在某个方法中使用,我们可以在方法中定义内部类
局部内部类的另一个优点就是可以访问方法中的局部变量(必须事实上是final的,即在匿名类外部赋值后不能再改变了)
匿名内部类
在局部内部类的基础上再进一步,有时我们不需要创建多个内部类对象,只需要一个,那么使用匿名内部类会更加方便
例如我们在Java多线程中使用Runnable
接口时就是如此。
demo
假如说有一个计算接口
public interface Calculate { int cal(Integer num1, Integer num2); }
我们想实现一个加法器
- 常规内部类写法
public class Outer3 { class Adder implements Calculate{ @Override public int cal(Integer num1, Integer num2) { return num1 + num2; } } public Calculate getAdder() { return new Adder(); } }
测试一下:
Outer3 outer3 = new Outer3(); Calculate adder = outer3.getAdder(); int r1 = adder.cal(1,2); // 3
- 匿名内部类写法
public class Outer3 { public Calculate getAdder() { return new Calculate() { @Override public int cal(Integer num1, Integer num2) { return num1 + num2; } }; } }
同样的测试:
Outer3 outer3 = new Outer3(); Calculate adder = outer3.getAdder(); int r1 = adder.cal(1,2); // 3
- 匿名内部类 + Java8
由于我们的Calculate
只有一个接口,所以我们还可以用Java8的lambda表达式再简化一下
public class Outer3 { public Calculate getAdder() { return (num1, num2) -> num1 + num2; } }
测试方法还是不变
Outer3 outer3 = new Outer3(); Calculate adder = outer3.getAdder(); int r1 = adder.cal(1,2); // 3
- 直接写在方法里面
当这个内部类的对象并不需要复用的地方,我们也可以直接在使用到它的地方编写匿名内部类对象
Calculate adder = (num1, num2) -> num1 + num2; int calRes = adder.cal(1,2); // 3
初始化
匿名内部类因为没有名字,所以你没有办法通过构造函数进行初始化,但我们还是有一些其他的方法来达到相同的效果
- 继承一个基类
还是用上面那个加法器的例子
public abstract class AbstractCalculate implements Calculate { public int baseNum; public AbstractCalculate(int i) { baseNum = i; } }
测试一下
// 此处的 1 可修改为从外部传参 Calculate adder = new AbstractCalculate(1) { @Override public int cal(Integer num1, Integer num2) { return num1 + num2 + baseNum; } }; int res = adder.cal(1,2); // 4
即通过父类来进行初始化并使用父类的成员
- 代码块
继承的方式要求必须有一个父类,有一定的限制,另外一种方法就是通过代码块的方式
public class Outer3 { public Calculate getAdder() { return (num1, num2) -> num1 + num2; } // 这里我们在初始化的时候做一些复杂的事情,比如初始化一个对象 public Calculate getAdderWithInit(int base) { Calculate adder = new Calculate() { Calculate _adder; { _adder = getAdder(); } @Override public int cal(Integer num1, Integer num2) { return _adder.cal(num1,num2) + base; } }; return adder; } }
测试一下:
Calculate adder = new Outer3().getAdderWithInit(2); int res = adder.cal(1,2); // 5
静态内部类
有时我们只是为了把一个类隐藏在另一个类中,并不需要内部类应用外部类的对象,此时我们就可以将内部类声明为static的
普通的内部类对象隐式地保存了一个引用,指向创建它的外部类对象。然而,当内部类是 static 的时,就不是这样了。
嵌套类(静态内部类)意味着:
- 创建嵌套类的对象时,不需要其外部类的对象。
- 不能从嵌套类的对象中访问非静态的外部类对象。
嵌套类与普通的内部类还有一个区别:
普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有 static 数据和 static 字段,也不能包含嵌套类。
但是嵌套类可以包含所有这些东西:
接口内部类
嵌套类可以作为接口的一部分。你放到接口中的任何类都自动地是 public 和 static 的。因为类是 static 的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则。你甚至可以在内部类中实现其外部接口
如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便
---- 《OnJava8》
内部类作用小结
命名
使用内部类命名可以避免命名重复的问题
例如Element这样一个类,你在很多类中可能都会使用到,为了避免重复你可能会使用AElement,BElement这种方式,但是使用内部类则可以使代码结构更加清晰一些
代码结构
- 当两个类的关系特别紧密时,通过内部类可以使得代码结构更加清晰
- 同时外部类可以少传递很多参数,因为内部类可以直接访问外部类的成员
如果不使用外部类,我们就需要使用组合的方式来实现对象的复用,相比使用内部类的缺点:
- 内部类的对象其实只有外部类会使用,但如果不使用内部类的方式,你就无法用private来修饰类,最多只能用default,即将这个类暴露给其他类了
- 外部类使用内部类的对象方法时需要传递很多参数过去
访问控制
即在访问权限中提到过的,可以隐藏内部类,当我的内部类实现了某个接口时,则相当于外部类隐藏了接口的实现,比较典型的就是Iterator
"多重继承"
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。 ---- 《OnJava8》
- 内部类实现接口:如果内部类只是实现接口,那么由外部类自己实现,功能上其实并没有太大的区别
- 内部类继承抽象类:这个就只有内部类才能完成了,因为Java是单继承的体系
参考资料
《OnJava8》
《Java核心技术:卷1》
这篇关于Java基础 - 内部类的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)