Java 中的内部类
这篇文章主要包括下面几个方面的内容
- Java 内部类的意义
- Java 中内部类的分类
- Java 中内部类在设计模式中的应用
- 关于Java内部类的一些思考
Java 中内部类的意义
- 完善多态性
Java 设计思想是要继承前面的面向对象语言中的优良之处和避免其他语言中的一些缺点,所以Java没有使用多重继承的思想,Java使用的是单继承模型和接口来完善它的多态性。
public class Children implements Father, Mother {
}
如果是两种接口的的话这样还不错,如果是两种类的话,这这样的写法是根本不行的。
public class Children extends Father {
class MotherGene extends Mother{
}
}
可以考虑使用这样的方法,同时在一个类中实现各种不同的接口
class NetWorkUtil{
class Get implement IGET{
}
class Post implement IPost{
}
}
- 状态信息独立
作为外围类中定义的一个类,不像属性和方法,这种类是完善的。就像Egg
类中的Yolk
一样,虽然是两种不同的东西,但是有着紧密的关系
Java 中的内部类分类
成员内部类
public class Test {
public String contentString = "";
private void wrapContent(){}
//Content 类
private class Content{
// private static String factory = "淘宝";
private void test(){
contentString = "nothing";
wrapContent();
}
}
//Desination 类
protected class Destination{
}
}
内部类中是不可以定义static
成员的,这个下面会详细说。
成员内部类对外部类的访问:
成员内部类中这个内部类就像外部类的一个成员方法一样,可以调用外部类的方法和属性。
外部类对成员内部类的访问:
private void accessContent(){
Content content = new Content();
content.test();
}
容易理解和想到,访问内部类的成员方法和属性需要首先实例化这个成员类
在外围类的外部实例化内部类
这个时候需要首先实例化外部类再实例化这个内部类,这说明内部类的初始化和外部类的初始化是绑定在一起的:
public static void main(String args[]){
Test test = new Test();
Test.Destination destination = test.new Test.Destination();
}
下面我们再看一个例子,关于内部类的继承:
public class B {
public class Base{
}
//在一个同一个类中的继承
public class DerivedB extends Base{
public DerivedB(){
}
}
}
B中的DerivedB
继承了Base
,他的构造函数不需要参数。
public class A {
//外部继承的时候需要和外部类绑定
public class DerivedA extends B.DerivedB{
public DerivedA(B b){
b.super();
}
}
}
在另外的一个类A
中,继承B
中的DerivedB
,它的构造函数必须有一个B
作为参数,并且首先要b.super()
。这个例子进一步地验证了上面的说法,内部类的初始化必须是以外部类的初始化为其前提的。
同时解释了上面的问题,static
成员会随着这个类的加载而加载,然而一个非static
的内部类不会首先被加载,除非外部类实例化之后再new它。所以内部类中持有一个static member
是和Java 加载机制矛盾的。
关于成员类的访问修饰符
private
protected
-
public
作一个类的一个成员,成员内部类的修饰符上没有很大差别
局部内部类
局部内部类是定义在作用域上的,常见的的就是定义在方法上。如果是那种一次性定义的类的话在这里定义比较清晰:
继续在Test
类中增加定义
public class Wrapper{
}
private Wrapper wrapContent(){
class PaperWrapper extends Wrapper{
private Content content;
private Destination destination;
public PaperWrapper(Content content, Destination destination){
this.content = content;
this.destination = destination;
}
}
return new PaperWrapper(new Content(), new Destination());
}
这个地方需要注意个问题,如果没有定义PaperWrapper
,但是想返回这个对象的话是不行的。
局部类的访问修饰符
局部类不可以使用访问修饰符,就和定义在方法和其他局部块中的内容一样,包括static
。
private void try2createStaticWrapper(){
// 不可以static member
// int static a;
// static class StaticContent{
//
// }
}
虽然局部类是定义在方法的局部,但是不是在使用方法的时候才编译,也不是方法结束之后就回收。
匿名内部类
之所以叫做匿名内部类,是因为这个类是没有名字的,直接将Runnable
接口实现。匿名内部类的用法大家很熟悉了,普通情况下作为一个接口的实现,传递到回调函数中。
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
匿名内部类中的final
Java8 之前特别是在匿名内部类中需要给定义在匿名类外面的内容加上final,如果是复杂的场景需要通过回调改变外面的值的话,有时候还是很恶心人的,
public interface Listener{
}
//内部类中定义的对象希望在外部是final的
public void test3(final String parameter){
new Listener(){
String s = not_final_member;
String s2 = parameter;
// parameter = "test";
};
}
Java8 之后允许你不加final
了,通过编译器的智能在需要的时候添加。
Java 内部类在设计模式中的应用
- 迭代器模式
import java.util.ArrayList;
public class Array {
private int index;
private ArrayList<String> list = new ArrayList<>();
private Iterator iterator;
public Array(){
index = 0;
list.add("one");
list.add("two");
list.add("three");
list.add("four");
iterator = new Iterator() {
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public String next() {
return list.get(index++);
}
};
}
public static void main(String args[]){
Array array = new Array();
for (;array.iterator.hasNext();){
System.out.println(array.iterator.next());
}
}
}
使用了匿名内部类;
工厂方法
抽象产品
public interface Service {
void method1();
}
抽象工厂:
public interface Factory {
Service getService();
}
完成一个产品的实现:
public class ServiceImplemention implements Service {
@Override
public void method1() {
System.out.println("use service");
}
public static Factory factory = new Factory() {
@Override
public Service getService() {
return new ServiceImplemention();
}
};
}
所以只要知道产品的名字就可以去生产。
想法和总结
Java中让我使用的最不爽的一点就是匿名内部类,这样的写法其实对于使用者是一种痛苦,虽然逻辑上很清楚,写起来不方便,不过lamda表达式和类型推断对这里是非常大的改善。
public interface ListenerWithParams{
void test(String para1, String[] param2);
}
public static void main(String args[]){
Runnable runnable = () -> {
};
ListenerWithParams listenerWithParams = (string, array) ->{
//do something ;
};
}
读 《Thinking in Java》有感随记之