参考:
Java核心技术
Java编程思想
https://www.cnblogs.com/dolphin0520/p/3811445.html
https://www.cnblogs.com/chenssy/p/3388487.html
局部内部类:http://baijiahao.baidu.com/s?id=1600724548575261991&wfr=spider&for=pc
外部类和内部类数据访问:https://blog.csdn.net/weixin_40707866/article/details/79652084
内部类
内部类的概念
将一个类的定义放在另一个类的定义内部,这就是内部类。虽然内部类存在于外部类定义之中,但是编译之后还是会产生属于自己的.class文件,依旧遵循着每个类都会产生一个.class文件。一般来说内部类包括四种:成员内部类、局部内部类、匿名内部类和嵌套类(也叫静态内部类)。其中局部内部类定义在方法或作用域内,静态内部类使用static修饰。
内部类与外部类之间的数据访问
内部类就相当于一个外部类的成员变量,所以可以直接访问外部类的成员,包括成员变量和成员函数。但是外部类不能直接访问内部类成员,必须通过创建内部类实例的方法进行访问。
为什么使用内部类
如果没有内部类提供的,可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。使用内部类的最大好处就是它使得Java的多重继承的解决方案更加完整,因为内部类允许继承多个非接口类型(即类和抽象类)。
当多重继承应用于接口时,那么看不出使用内部类的必要性。因为使用多实现仍然可以实现功能,但是如果拥有的是抽象类或具体的类,那么就只能使用内部类实现多重继承。
package learninnerclass;
interface A{}
interface B{}
class X implements A,B{ }
class Y implements A{
B makeB(){
return new B(){};
}
}
public class MultiInterfaces {
static void takesA(A a){}
static void takesB(B b){}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
takesA(x);
takesA(y);
takesB(x);
takesB(y.makeB());
}
}
只能使用内部类实现多重继承的情况
package learninnerclass;
class D{}
abstract class E{}
class Z extends D{
E makeE(){ return new E(){};}
}
public class MultiImplementation {
static void takesD(D d){}
static void takesE(E e){}
public static void main(String[] args){
Z z = new Z();
takesD(z);
takesE(z.makeE());
}
}
.this和.new
内部类的对象有一个隐式引用(即.this),它引用了实例化该内部对象的外部类对象。通过这个引用,可以访问外部类对象的全部状态。这个引用在内部类的定义中是不可见的,是由编译器自动生成的。具体使用如下:
外部类名.this; // 该表达式表示外部类的引用
外部类对象.new 内部类名(构造方法的参数); // 该表达式表示创建内部类对象
外部类名.内部类名; // 该表达表示在外部类的作用域之外,引用内部类
成员内部类
在成员内部类中要注意两点,1) 成员内部类中不能存在任何static的变量和方法,static只能用在静态常量的声明上;2)成员内部类是依附于外部类的,所以只有先创建了外部类对象才能够创建内部类对象。
在成员内部类的定义中可以对外部类所有元素进行访问,即使该元素使用private修饰。其实这就相当于将成员内部类当做外部类的一个元素(成员)了。正因为如此,所以可以使用private、包访问权限、protected、public等权限修饰符来修饰成员内部类,而不像外部类只能使用public或包访问权限来修饰。
在非静态内部类中,不能定义静态的成员变量和成员方法,包括成员内部类,局部内部类,匿名局部类,但可以在静态内部中定义静态的成员变量和方法。值得注意的是,可以在所有的内部类中定义静态常量,即static final。
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类定义中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向该内部类对象的引用来访问内部类的域或者方法。
不过需要注意的是,当成员内部类拥有和外部类同名的成员变量或方法时,会发生隐藏现象。默认情况下,访问的是成员内部类的成员。如果想要访问外部类的同名成员,则需要使用下面的形式进行访问。
示例
一般示例
package com.thinkinginjava.chapter10;
public class Parcel3 { // 外部类
class Contents{ // 成员内部类
private int i = 11;
public int value() { return i; }
}
class Destination{// 成员内部类
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label;}
}
public static void main(String[] args) {
Parcel3 p = new Parcel3(); // 生成外部类对象
// 必须通过外部类对象才能创建内部类对象
Parcel3.Contents c = p.new Contents(); // 生成Contents成员内部类对象
System.out.println(c.value());
Parcel3.Destination d = p.new Destination("Suzhou");// 生成Destination成员内部类对象
System.out.println(d.readLabel());
}
}
访问同名属性
class MemberInnerClass{
private String str = "外部类同名属性";// 与内部类数据有重名
class Contents{ // 成员内部类
private String str = "内部类同名属性";
public String value() { return str; }// 只会访问到内部类的同名属性
// 要在内部类中访问外部类同名属性,需要借助外部类对象
public String FatherValue() { return new MemberInnerClass().str; }
}
public void useInnerClass(){
System.out.println("在外部类中,str = "+str); //访问同名变量(只会访问到外部类的)
// 要想在外部类中访问内部类同名属性需要借助内部类对象
Contents c = new Contents();
System.out.println("Contents中的域 :"+c.str);
System.out.println("Contents中的方法 value: "+ c.value());
}
}
public class MemberInnerClassTest {
public static void main(String[] args) {
MemberInnerClass mic = new MemberInnerClass();
MemberInnerClass.Contents c = mic.new Contents();
System.out.println("通过内部类的方法访问内部类的属性 str:"+c.value());
System.out.println("通过内部类的方法访问外部类的属性 str:"+c.FatherValue());
System.out.println("*****************");
mic.useInnerClass();
}
}
匿名内部类
匿名内部类的特点:1)没有名字的局部内部类;2)匿名内部类是一种没有构造器的类;3)匿名内部类没有访问修饰符和static修饰符;4)匿名内部类其实是隐式地继承某一个父类或者实现某一个接口。这句话的意思就是如果声明匿名内部类,前提是这个父类或者接口必须先存在。如下代码所示,Contents是一个早已存在的接口,如果Contents接口不存在则无法创建匿名内部类。换句话说,匿名内部类是实现一个接口或者继承一个父类并重写的简洁方式。
示例
package learninnerclass;
// 已存在的一个接口
interface Contents{
int value();
}
public class AnonymousInnerClassTest {
public Contents contents() {
return new Contents() { // 匿名内部类
private int i = 11;
public int value() { return i;}
};
}
public static void main(String[] args) {
AnonymousInnerClassTest p = new AnonymousInnerClassTest();
Contents c = p.contents();
System.out.println(c.value());
}
}
其实上面的代码,如果不使用匿名内部类,则等价于下面的写法。
public class Parcel7b {
// 继承接口并实现
class MyContents implements Contents{
private int i =11;
public int value() { return i; }
}
public Contents contents() { return new MyContents(); }
public static void main() {
Parcel7b p = new Parcel7b();
Contents c = p.contents();
System.out.println(c.value());
}
}
局部内部类
局部内部类定义在方法或作用域(代码块)内,不能使用任何访问修饰符。局部内部类的优势在于,除包含该类的方法,其他代码均不知道局部内部类的存在。同其他内部类比较,局部内部类不仅可以访问包含它们的外围类,而且还可以访问局部变量。
1) 如果局部内部类定义在静态方法中,它可以访问外部类中所有静态成员,包含私有。
2) 如果局部内部类定义在实例方法中,它可以访问外部类中所有的成员,包含私有。
除此之外,局部内部类还可以有构造方法。如果局部内部类要访问局部变量,那么局部变量必须声明为final类型。在实践中,局部内部类是所有内部类中最少使用的一种形式。
示例
package learninnerclass;
interface Counter{
int next();
}
public class LocalInnerClass {
private int count = 10;
private static double salary = 13.14;
public int getNum(){
return count;
}
public static double getSalary(){
return salary;
}
// 1.在实例方法中的局部内部类
Counter getCounter(final String name)
{
class LocalCounter implements Counter
{ //局部内部类中可以有构造方法
public LocalCounter(){
System.out.println("局部内部类的构造器...");
}
@Override
public int next() {
System.out.println("name = "+name);
// 1.1 实例方法中的局部内部类能访问外部类的实例变量和实例方法
System.out.println("count = "+count);
System.out.println("通过方法获取count = "+getNum());
// 1.2 实例方法中的局部内部类能访问外部类的静态变量和静态方法
System.out.println("salary = "+salary);
System.out.println("通过方法获取salary = "+getSalary());
return count++;
}
}
return new LocalCounter();
}
// 2.在静态方法中的局部内部类
static Counter getCounter2(final String name)
{
class LocalCounter implements Counter
{ //局部内部类中可以有构造方法
public LocalCounter(){
System.out.println("局部内部类的构造器...");
}
@Override
public int next() {
System.out.println("name = "+name);
// 2.1 静态方法中的局部内部类不能访问外部类的实例变量和实例方法
// System.out.println("count = "+count);
// System.out.println("通过方法获取count = "+getNum());
// 2.2 静态方法中的局部内部类只能访问外部类的静态变量和静态方法
System.out.println("salary = "+salary);
System.out.println("通过方法获取salary = "+getSalary());
return 0;
}
}
return new LocalCounter();
}
public static void main(String[] args)
{
LocalInnerClass lic = new LocalInnerClass();
Counter c1 = lic.getCounter("实例方法中的局部内部类");
c1.next();
Counter c2 = lic.getCounter2("静态方法中的局部内部类");
c2.next();
}
}
静态内部类(嵌套类)
如果不需要内部类对象与其对应的外部类对象之间存在联系,那么就可以将内部类声明为static的,以便消除产生的引用。换句话说,当内部类是静态内部类的时候,它并不会隐式地保存this引用(该引用指向创建它的外部类对象,普通内部类访问外部类对象的数据也是通过该引用才能够实现。),因此静态内部类对象和外部类对象之间也就不存在联系了。
如果创建了一个静态内部类,那就意味着:
1) 要创建静态内部类的对象,并不需要其外部类的对象;
2) 在静态内部类中不能访问外部类的非静态(static)的成员变量和成员方法,只能访问静态的成员变量和静态成员方法。
示例
package learninnerclass;
public class StaticInnerClassTest02 {
private int num =10;
private static double salary=100.4;
public int getNum(){
return num;
}
public static double getSalary(){
return salary;
}
public static class Inner{
// 1.访问外围类的成员变量
// 1.1 在静态内部类中不能访问外部类的非静态属性
// public void info1(){// 这段代码是错误的
// System.out.println("num : "+num);
// }
// 1.2访问外围类的静态成员变量
public void info2(){// 可以访问外部类的静态成员变量
System.out.println("salary : "+salary);
}
// 2.访问外围类的成员方法
// 2.1 在静态内部类中不能访问外部类的非静态方法
// public void info3(){// 这段代码是错误的
// System.out.println("num :"+ getNum());
// }
// 2.2 在静态内部类中可以访问外部类的静态方法
public void info4(){// 可以访问外部类的静态成员方法
System.out.println("salary : "+getSalary());
}
}
public static void main(String[] args) {
// 创建静态内部类对象(创建静态内部类对象的时候并没有通过外部类对象,即:外部类对象.new 内部类名() )
StaticInnerClassTest02.Inner in = new Inner();
in.info2();
in.info4();
}
}
另外,声明在接口中的内部类也会自动成为static和public。