一:什么是内部类?
定义:
内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限)。内部类主要有以下几类:成员内部类、局部内部类、静态内部类、匿名内部类。
(1) 在类中定义一个类(私有内部类,静态内部类)
(2) 在方法中定义一个类(局部内部类,匿名内部类)
二、为什么需要内部类?
主要原因有以下几点:- 内部类方法可以访问该类定义所在的作用域的数据,包括私有的数据
- 内部类可以对同一个包中的其他类隐藏起来,一般的非内部类,是不允许有 private 与protected权限的。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷、方便编写线程代码
- 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。 每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。大家都知道Java只能继承一个类,它的多重继承在我们没有学习内部类之前是用接口来实现的。但使用接口有时候有很多不方便的地方。比如我们实现一个接口就必须实现它里面的所有方法。而有了内部类就不一样了。它可以使我们的类继承多个具体类或抽象类。
二、内部类解析
1、成员内部类
- 即在一个类中直接定义的内部类, 成员内部类与普通的成员没什么区别,可以与普通成员一样进行修饰和限制。成员内部类不能含有static的变量和方法。
- 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
- 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问。
- 由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。如果成员内部类用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。
package innerclass;
public class OutClassT1 {
private String outName = "outName";
private int sameAge = 10;
/**
* 不能定义静态成员、可以访问外部类的所有成员、内部类和外部类的实例变量可以共存
*/
class InnerClass {
public String innerName = "innerName";
//内部类和外部类的实例变量可以共存
public int sameAge = 20;
public void write() {
InnerClass clazz = new InnerClass();
//成员内部类可以任意访问外部类的成员变量
System.out.println("outName=" + outName);
//在内部类中访问内部类自己的变量
System.out.println("innerName=" + innerName);
//在内部类中访问内部类自己的变量也可以用this.变量名
System.out.println("sameAge=" + this.sameAge);
//在内部类中访问外部类中与内部类同名的实例变量用外部类名.this.变量名
System.out.println("sameAge=" + OutClassT1.this.sameAge);
}
}
/**
* 外部类非静态方法获取内部类实例变量,可以直接new,因为非静态方法是属于外部类实例对象的,调用该方法的时候说明外部类实例对象已经存在了
* 在外部一般通过这种方法获取内部类实例的
*/
public InnerClass getInnerClass() {
InnerClass innerClass = new InnerClass();
return innerClass;
}
/**
* 外部类静态方法访问内部类的实例变量,必须new外部类实例,再通过外部类的实例变量获取内部类实例
* 原因:成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象
*/
public static void callInnerPro() {
//InnerClass innerClass = new OutClassT1().getInnerClass();
InnerClass innerClass = new OutClassT1().new InnerClass();
System.out.println(innerClass.innerName);
}
public static void main(String[] args) {
/**
* 也可以通过如下两种方式获取内部类实例
* 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
*/
//InnerClass innerClass = new OutClassT1().new InnerClass();
InnerClass innerClass = new OutClassT1().getInnerClass();
innerClass.write();
new OutClassT1().callInnerPro();
}
}
- 为什么外部类可以创建内部类的对象?并且内部类能够方便的引用到外部类对象?
class innerclass.OutClassT1$InnerClass extends java.lang.Object{
//域
public java.lang.String innerName;
public int sameAge;
final innerclass.OutClassT1 this$0;
//构造器
InnerClass(innerclass.OutClassT1);
//方法
public void write( );
}
编译器会默认为成员内部类添加了一个指向外部类对象的引用。
我们在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的this$0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。
- 为什么内部类可以引用外部类的私有域?
对OutClassT1进行java解析发现:为外部类的私有变量都添加了静态方法:
public class innerclass.OutClassT1 extends java.lang.Object{
//域
private java.lang.String outName;
private int sameAge;
//构造器
public OutClassT1( );
//方法
public static void main(java.lang.String[]);
static int access$100(innerclass.OutClassT1);
static java.lang.String access$000(innerclass.OutClassT1);
public innerclass.OutClassT1$InnerClass getInnerClass( );
public static void callInnerPro( );
}
static int access$100(innerclass.OutClassT1);
static java.lang.String access$000(innerclass.OutClassT1);
它将返回值作为参数传递给他的对象域data。这样内部类InnerClass中的打印语句:System.out.println(outName);
实际上运行的时候调用的是: System.out.println(this$0.access$000(OutClassT1));
总结一下编译器对类中内部类做的手脚吧:
(1) 在内部类中偷偷摸摸的创建了包可见构造器,从而使外部类获得了创建权限。
(2) 在外部类中偷偷摸摸的创建了访问私有变量的静态方法,从而 使 内部类获得了访问权限。
这样,类中定义的内部类无论私有,公有,静态都可以被包围它的外部类所访问。
2、局部内部类
- 在方法中定义的内部类称为局部内部类。
- 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,和此外围类所有的成员。
- 可以定义与外部类同名的变量、局部内部类中不可以定义静态变量、局部内部类中可以访问外部类的变量。
- 因为方法中的内部类没有访问修饰符, 所以方法内部类对包围它的方法之外的任何东西都不可见,故:局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
- 局部内部类对象不能使用该内部类所在方法的非final局部变量。
public class OutClassT3 {
public String outName = "outName";
private String sameName= "SameName from out";
public void outMethod(final String outMethodInput) {
final String outMethodName = "outMethodName";
/**
* 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的
* 可以定义与外部类同名的变量
* 局部内部类中不可以定义静态变量
* 可以访问外部类的变量
* 访问所在外部类方法的变量,则该变量必须是final类型的
*/
class InnerClass {
//可以定义与外部类同名的变量
public String sameName = "SameName from inner";
//局部内部类中不可以定义静态变量
//public static int age;
void innerMethod() {
//可以直接访问外部类的变量
System.out.println("outName=" + outName);
//可以直接访问自己的变量
System.out.println("sameName=" + sameName);
//访问和内部类变量重名的外部类变量
System.out.println("sameName=" + OutClassT3.this.sameName);
//访问所在外部类方法的变量,则该变量必须是final类型的
System.out.println("outMethodName=" + outMethodName);
//访问所在外部类方法的变量,则该变量必须是final类型的
System.out.println("outMethodInput=" + outMethodInput);
}
}
/**
* 局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
*/
new InnerClass().innerMethod();
}
public static void main(String[] args) {
OutClassT3 outClassT3 = new OutClassT3();
outClassT3.outMethod("outMethodInput");
}
}
3、匿名内部类
简单地说:匿名内部类就是没有名字的内部类。匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。一般来说,匿名内部类用于继承其他类或是实现接口,不需要增加额外的方法,只是对继承方法的实现或是重写。
比较常用的场景是:为组件添加监听功能、多线程开发。
- 什么情况下需要使用匿名内部类?如果满足下面的一些条件,使用匿名内部类是比较合适的:
- 只用到类的一个实例。
- 类在定义后马上用到。
- 类非常小(SUN推荐是在4行代码以下)
- 给类命名并不会导致你的代码更容易被理解。
- 匿名内部类不能有构造方法。
- 匿名内部类不能定义任何静态成员、方法和类。
- 匿名内部类不能是public,protected,private,static。
- 只能创建匿名内部类的一个实例。
- 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
- 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
4、静态内部类(也称之为嵌套类)
如果不需要内部类对象与其外围类对象之间有联系,可以将内部类声明为static。
普通的内部类对象隐含地保存了一个引用,指向创建它的外围类对象。然而,当内部类是static的时,静态内部类是不需要依赖于外部类,这意味着:
- 要创建嵌套类的对象,并不需要其外围类的对象。
- 不能从嵌套类的对象中访问非静态的外围类对象。
生成一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。
public class OutClassT3 {
public String outName = "outName";
private String sameName= "SameName from out";
public void outMethod(final String outMethodInput) {
final String outMethodName = "outMethodName";
/**
* 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的
* 可以定义与外部类同名的变量
* 局部内部类中不可以定义静态变量
* 可以访问外部类的变量
* 访问所在外部类方法的变量,则该变量必须是final类型的
*/
class InnerClass {
//可以定义与外部类同名的变量
public String sameName = "SameName from inner";
//局部内部类中不可以定义静态变量
//public static int age;
void innerMethod() {
//可以直接访问外部类的变量
System.out.println("outName=" + outName);
//可以直接访问自己的变量
System.out.println("sameName=" + sameName);
//访问和内部类变量重名的外部类变量
System.out.println("sameName=" + OutClassT3.this.sameName);
//访问所在外部类方法的变量,则该变量必须是final类型的
System.out.println("outMethodName=" + outMethodName);
//访问所在外部类方法的变量,则该变量必须是final类型的
System.out.println("outMethodInput=" + outMethodInput);
}
}
/**
* 局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
*/
new InnerClass().innerMethod();
}
public static void main(String[] args) {
OutClassT3 outClassT3 = new OutClassT3();
outClassT3.outMethod("outMethodInput");
}
}
public class Test {
public static void main(String[] args) {
/**
* 需要通过生成外部类对象来生成内部类实例
*/
OutClassT2.StaticInnerClass innerClass = new OutClassT2.StaticInnerClass();
//这里只能调用public类型的方法
innerClass.innerMethod();
//不能调用私有方法,这里方法的修饰符和平时普通类的修饰符意义一样
//innerClass.privatInnerMethod();
// 调用静态内部类的静态方法,直接调用
OutClassT2.StaticInnerClass.staticInnerMethod();
}
}
参考:
https://www.cnblogs.com/ITtangtang/p/3980460.html
http://www.cnblogs.com/dolphin0520/p/3811445.html
https://www.cnblogs.com/yanzige/p/5377332.html
http://blog.51cto.com/android/384809
《java编程思想》
附:JAVA分析器代码:
package tools;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;
public class ClassAnalyzer {
private static final String tab = " ";//缩进
/**
* 调用该方法,并传递class文件名称即可,比如ClassAnalyzer.analyzer("OutClassT1");//OutClassT1对应的是编译后生成的class文件名称
* @param className
* @throws ClassNotFoundException
*/
public static void analyzer(String className) throws ClassNotFoundException {
Class c = Class.forName(className);
System.out.print(Modifier.toString(c.getModifiers()));
System.out.print(" ");
System.out.print(c.toString());
Class superC = c.getSuperclass();
if (superC != null) {
System.out.print(" extends " + superC.getName());
}
System.out.println("{");//类开始括号
//打印域
System.out.println(tab + "//域");
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
printField(field);
}
//打印构造器
System.out.println(tab + "//构造器");
Constructor[] constructors = c.getDeclaredConstructors();
for (Constructor constructor : constructors) {
printConstructor(constructor);
}
//打印方法
System.out.println(tab + "//方法");
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
printMethod(method);
}
System.out.println("}");//类结束括号
}
//打印域
private static void printField(Field field) {
System.out.print(tab);
System.out.print(Modifier.toString(field.getModifiers()));
System.out.print(" ");
Class fieldType = field.getType();
if (fieldType.isArray()) {
System.out.print(getArrayTypeName(fieldType));
} else {
System.out.print(field.getType().getName());
}
System.out.print(" ");
System.out.print(field.getName());
System.out.println(";");
}
//打印构造器
private static void printConstructor(Constructor constructor) {
System.out.print(tab);
System.out.print(Modifier.toString(constructor.getModifiers()));
System.out.print(" ");
System.out.print(constructor.getDeclaringClass().getSimpleName());
Class[] varTypes = constructor.getParameterTypes();
System.out.print("(");
printParameters(varTypes);
System.out.println(");");
}
//打印方法
private static void printMethod(Method method) {
System.out.print(tab);
System.out.print(Modifier.toString(method.getModifiers()));
System.out.print(" ");
Class returnType = method.getReturnType();
if (returnType.isArray()) {
System.out.print(getArrayTypeName(returnType));
} else {
System.out.print(method.getReturnType().getName());
}
System.out.print(" ");
System.out.print(method.getName());
System.out.print("(");
Class[] varTypes = method.getParameterTypes();
printParameters(varTypes);
System.out.print(")");
//声明抛出的异常
Class[] exceptionType = method.getExceptionTypes();
if (exceptionType.length != 0) {
System.out.print(" throws ");
for (int i = 0; i < exceptionType.length; i++) {
System.out.print(exceptionType[i].getName());
if (i < (exceptionType.length - 1)) {
System.out.print(",");
}
}
}
System.out.println(";");
}
//打印构造器和方法的参数列表
private static void printParameters(Class[] varTypes) {
if (varTypes.length > 0) {
for (int i = 0; i < varTypes.length; i++) {
if (varTypes[i].isArray()) {
System.out.print(getArrayTypeName(varTypes[i]));
} else {
System.out.print(varTypes[i].getName());
}
if (i < (varTypes.length - 1)) {
System.out.print(", ");
}
}
} else {
System.out.print(" ");
}
}
public static String getArrayTypeName(Class type) {
StringBuffer buffer = new StringBuffer(getArrayType(type).getName());
int dimension = countArrayDimension(type);
for (int i = 1; i <= dimension; i++) {
buffer.append("[]");
}
return buffer.toString();
}
public static int countArrayDimension(Class type) {
int dimension = 0;
if (type.isArray()) {
Class tempType = type;
while ((tempType = tempType.getComponentType()) != null) {
dimension++;
}
}
return dimension;
}
public static Class getArrayType(Class type) {
Class arrayType = null;
if (type.isArray()) {
Class tempType = type.getComponentType();
do {
arrayType = tempType;
} while ((tempType = tempType.getComponentType()) != null);
}
return arrayType;
}
public static void main(String[] args) {
try {
// Scanner in = new Scanner(System.in);
// System.out.print("Input class name:");
// String className = in.next();
// in.close();
System.out.println("args = [" + 11111 + "]");
String className = "com.wisely.learn.generic.DateInterval";
analyzer(className);
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}
}