参考资料
[1]. 疯狂Java讲义(第三版) 李刚
内部类
非静态内部类
内部类因为是视同类成员,所以有四种权限,定义语法格式如下:
[public|protected|private] class 类名
{
// 类体
}
示例程序如下:
package com.qunar;
/**
* Cow interface
*
* @author shuaige
* @date 2018/01/23
*/
public class Cow {
/**
* 重量
*/
private double weight;
/**
* 外部类的构造器
*/
public Cow(){}
/**
* 外部类的构造器
* @param weight
*/
public Cow(double weight){
this.weight = weight;
}
/**
* 定义一个非静态内部类
*/
class Cowleg
{
/**
* 非静态内部类的实例变量,长
*/
private double length;
/**
* 非静态内部类的实例变量,颜色
*/
private String color;
/**
* 内部类的构造器
*/
public Cowleg(){}
/**
* 内部类的构造器
*/
public Cowleg(double length, String color)
{
this.length = length;
this.color = color;
}
/**
* getLength
* @return double
*/
public double getLength() {
return length;
}
/**
* setLength
* @param length
*/
public void setLength(double length) {
this.length = length;
}
/**
* getColor
* @return
*/
public String getColor() {
return color;
}
/**
* setColor
* @param color
*/
public void setColor(String color) {
this.color = color;
}
/**
* 非静态内部类的实例方法
*/
public void info()
{
System.out.println("当前牛腿的颜色是:" + color + "高:" + length);
// 直接访问外部类的private修饰的成员变量
System.out.println("本牛腿所在奶牛重:" + weight);
}
}
/**
* 测试方法
*/
public void test()
{
Cowleg cl = new Cowleg(1.12, "黑白相间");
cl.info();
}
/**
* 执行
* @param args
*/
public static void main(String[] args){
Cow cow = new Cow(378.9);
cow.test();
}
}
在内部类里,有下面的代码:
// 直接访问外部类的private修饰的成员变量
System.out.println("本牛腿所在奶牛重:" + weight);
在CowLeg类的方法内直接访问其外部类的private实例变量,这是因为在非静态内部类对象里,保存了一个它所寄生的外部类对象的引用(当调用非静态内部类的实例时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里)。如下图所示:
不同类型的变量的访问方式:
/**
* DiscernVariable class
*
* @author shuaige
* @date 2018/01/23
*/
public class DiscernVariable {
/**
* 外部类的实例变量
*/
private String prop = "外部类的实例变量";
/**
* InClass class
*
* @author shuaige
* @date 2018/01/23
*/
private class InClass
{
/**
* 内部类的实例变量
*/
private String prop = "内部类的实例变量";
public void info()
{
/**
* 局部变量
*/
String prop = "局部变量";
// 通过外部类类名.this.varName 访问外部类实例变量
System.out.println("外部类的实例变量值:" + DiscernVariable.this.prop);
// 通过this.varName访问内部类实例的变量
System.out.println("内部类的实例变量值:" + this.prop);
// 直接访问局部变量
System.out.println("局部变量的值:" +prop);
}
}
/**
* 测试方法
*/
public void test()
{
InClass in = new InClass();
in.info();
}
/**
* 执行方法
* @param args
*/
static public void main(String[] args)
{
new DiscernVariable().test();
}
}
如果需要从外部类访问内部类里的细节,代码如下:
/**
* Outer class
*
* @author shuaige
* @date 2018/01/23
*/
public class Outer {
private int outProp = 9;
class Inner
{
private int inProp = 5;
public void acessOuterProp()
{
// 非静态内部类可以直接访问外部类的private成员变量
System.out.println("外部类的outProp值:" + outProp);
}
}
public void accessInnerProp()
{
// 外部类不能直接访问非静态内部类的实例变量
// 下面代码出现编译错误
// System.out.println("内部类的inProp值:" + inProp);
// 如需访问内部类的实例变量,必须显示创建内部类对象
System.out.println("内部类的inProp值:" + new Inner().inProp);
}
/**
* 执行类
* @param args
*/
public static void main(String[] args)
{
Outer out = new Outer();
out.accessInnerProp();
}
}
在静态方法里无法访问作为实例类成员的内部类,示例代码如下:
/**
* StaticTest class
*
* @author shuaige
* @date 2018/01/23
*/
public class StaticTest {
/**
* 内部类
*/
private class In{}
/**
* 执行方法
* @param args
*/
public static void main(String[] args)
{
// 下面代码引发编译异常,因为静态成员(main()方法)
// 无法访问非静态成员(In类)
new In();
}
}
Java不允许在非静态内部类里定义静态成员,代码如下:
/**
* InnerNoStatic class
*
* @author shuaige
* @date 2018/01/23
*/
public class InnerNoStatic {
private class InnerClass
{
/**
* 下面三个静态声明都将引发如下编译错误:
* 非静态内部类不能有静态声明
*/
/**
* 静态初始化块
*/
static
{
System.out.println("--------");
}
/**
* 静态成员变量inProp
*/
private static int inProp;
/**
* 静态方法test
*/
private static void test(){}
}
}
静态内部类
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用static修饰的内部类被称为类内部类或静态内部类。
静态内部类只能访问外部类的静态成员,代码如下:
/**
* StaticInnerClassTest class
*
* @author shuaige
* @date 2018/01/25
*/
public class StaticInnerClassTest {
/**
* 实例变量
*/
private int prop1 = 5;
/**
* 静态变量
*/
private static int prop2 = 9;
/**
* StaticInnerClass class
*
* @author shuaige
* @date 2018/01/25
*/
static class StaticInnerClass
{
/**
* 可以包含静态成员变量
*/
private static int prop2;
static public void accessOuterProp()
{
// 下面代码出现错误
// 静态内部类无法访问外部类的实例变量
// System.out.println(prop1);
// 但可以访问静态变量
System.out.println(StaticInnerClassTest.prop2);
}
}
}
外部类如何访问静态内部类:
/**
* AccessStaticInnerClass class
*
* @author shuaige
* @date 2018/01/25
*/
public class AccessStaticInnerClass {
/**
* StaticInnerClass class
*
* @author shuaige
* @date 2018/01/25
*/
static class StaticInnerClass
{
/**
* 静态成员变量
*/
private static int prop1 = 5;
/**
* 非静态成员变量
*/
private int prop2 = 9;
}
/**
* 变量测试方法
*/
public void accessInnerProp()
{
// 不能直接访问静态内部类的成员变量
// System.out.println(prop1);
// 访问静态成员
System.out.println(StaticInnerClass.prop1);
// 访问实例成员
System.out.println(new StaticInnerClass().prop2);
}
}
使用内部类
在外部类内部使用内部类
不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员。
在外部类以外使用非静态内部类
内部类的权限列表:
1. 省略访问控制符的内部类,只能被与外部类处于同一个包中的其他类所访问。
2. 使用protected修饰的内部类,可被与外部类处于同一个包的其他类和外部类的子类所访问。
3. 使用public修饰符的内部类,可以在任何地方被访问。
4. 使用private修饰符的内部类,只能在外部类中使用。
在外部类以外的地方创建非静态内部类的对象,并把它赋给非静态内部类类型的变量,代码如下:
定义含有内部类的外部类
/**
* Out class
*
* @author shuaige
* @date 2018/01/25
*/
public class Out {
/**
* 定义一个内部类,不使用访问控制符,
*/
final class In
{
/**
*
* @param msg
*/
public In(String msg)
{
System.out.println(msg);
}
}
}
调用:
/**
* CreateInnerInstance class
*
* @author shuaige
* @date 2018/01/25
*/
public class CreateInnerInstance {
public static void main(String[] args)
{
/**
* 使用外部类访问内部类
*/
Out.In in = new Out().new In("测试信息");
/*
上面的代码可以可以改为如下三行代码
使用OutterClass.InnerClass的形式定义内部类变量
Out.In in;
创建外部类实例,非静态内部类实例将寄生在该实例中
Out out = new Out();
in = out.new In("测试信息");
*/
}
}
当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象。下面程序定义了一个子类继承了Out类的非静态内部类In类:
简单来说就是扩展内部静态类
/**
* SubClass class
*
* @author shuaige
* @date 2018/01/25
*/
public class SubClass extends Out.In{
/**
* 显示定义SubClass的构造器
* @param out
*/
public SubClass(Out out) {
// 通过传入的Out对象显式调用In的构造器
// 非静态内部类In类的构造器必须使用外部类对象来调用
// out代表外部类对象
// super代表调用In类的构造器
// 所以才能调用Out类的子类In类的构造器
out.super("hello");
}
}
在外部类以外使用静态内部类
定义一个带静态内部类的类
package com.qunar;
/**
* StaticOut class
*
* @author shuaige
* @date 2018/01/25
*/
public class StaticOut {
// 定义一个静态内部类,不使用访问控制符
// 即同一个包中的其他类可以访问该内部类
static class StaticIn
{
public StaticIn()
{
System.out.println("静态内部类的构造器");
}
}
}
调用的时候会调用构造器
/**
* CreateStaticInnerInstance class
*
* @author shuaige
* @date 2018/01/25
*/
public class CreateStaticInnerInstance {
public static void main(String[] args)
{
StaticOut.StaticIn in = new StaticOut.StaticIn();
/*
上面代码可改为如下两行代码
使用OuterClass.InnerClass的形式定义内部类变量
StaticOut.StaticIn in;
通过new来调用内部类构造器创建静态内部类实例
in = new StaticOut.StaticIn();
*/
}
}
局部内部类
局部内部类的使用不多,在实际开发中不建议使用,示例代码如下。
package com.qunar;
/**
* LocalInnerClass class
*
* @author shuaige
* @date 2018/01/25
*/
public class LocalInnerClass {
public static void main(String[] args)
{
/**
* 定义局部内部类
*/
class InnerBase
{
int a;
}
/**
* 定义局部内部类的子类
*/
class InnerSub extends InnerBase
{
int b;
}
// 创建局部内部类的对象
InnerSub is = new InnerSub();
is.a = 5;
is.b = 8;
System.out.println("InnerSub对象的a和b实例变量是:" + is.a + "," + is.b);
}
}
匿名内部类
匿名内部类适合创建只需要一次使用的类,匿名内部类的语法格式如下:
new 实现类() | 父类构造器(实参列表)
{
// 匿名内部类的类体部分
}
实现一个接口的匿名内部类,必须实现它的全部方法
package com.qunar;
/**
* AnonymousTest class
*
* @author shuaige
* @date 2018/01/23
*/
public class AnonymousTest {
/**
* 测试方法,此处因为使用了ProductClass接口
* 所以需要传入一个匿名类,来实现接口里面所有的方法
* @param p
*/
public void test(ProductClass p)
{
System.out.println("购买了一个" + p.getName() + ",花掉了" + p.getPrice());
}
public static void main(String[] args)
{
AnonymousTest ta = new AnonymousTest();
// 调用test方法时,需要传入一个Product参数
// 此处传入其匿名实现类的实例
ta.test(new ProductClass() {
/**
* 初始化块
*/
{
System.out.println("这里是初始化块");
}
/**
* 获取价格
* @return
*/
@Override
public double getPrice() {
return 1100;
}
/**
* 获取名称
* @return
*/
@Override
public String getName() {
return "iphone-x";
}
});
}
}
实现一个抽象类,代码如下:
抽象类
/**
* Device abstract class
*
* @author shuaige
* @date 2018/01/23
*/
abstract class Device {
/**
* 名称
*/
private String name;
/**
* 抽象方法getPrice
* @return
*/
public abstract double getPrice();
/**
* 无参构造器
*/
public Device(){}
/**
* 带参数构造器
* @param name
*/
public Device(String name)
{
this.name = name;
}
/**
* 获取name
* @return
*/
public String getName() {
return name;
}
/**
* 设置name
* @param name
*/
public void setName(String name) {
this.name = name;
}
}
实现示例:
/**
* AnonymousInner class
*
* @author shuaige
* @date 2018/01/23
*/
public class AnonymousInner {
public void test(Device d)
{
System.out.println("购买了一个" + d.getName() + ",花掉了" + d.getPrice());
}
public static void main(String[] args)
{
AnonymousInner ai = new AnonymousInner();
// 调用有参数的构造器创建Device匿名实现类的对象
ai.test(new Device("电子示波器") {
/**
* 获取价格
* @return
*/
@Override
public double getPrice() {
return 67.8;
}
});
// 调用无参数的构造器穿件Device匿名实现类的对象
Device d = new Device() {
// 初始化块
{
System.out.println("匿名内部类的初始化块...");
}
/**
* 获取价格
* @return
*/
@Override
public double getPrice() {
return 56.2;
}
/**
* 获取名称
* @return
*/
@Override
public String getName() {
return "键盘";
}
};
ai.test(d);
}
}
在Java 8之前,Java要求被局部内部类、匿名内部类访问的局部变量必须使用final修饰,Java 8更加智能,如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰,代码如下:
接口
/**
* InterfaceAA interface
*
* @author shuaige
* @date 2018/01/23
*/
public interface InterfaceAa {
/**
* test
*/
void test();
}
传入匿名类实现
/**
* ATest class
*
* @author shuaige
* @date 2018/01/23
*/
public class ATest {
public static void main(String[] args){
int age = 8;
InterfaceAa a = new InterfaceAa() {
@Override
public void test() {
System.out.println(age);
// 如果在这里再次改变age变量,将会报错
a = 100;
}
};
// 传入匿名类
a.test();
}
}
Java 8将这个功能称为“effectively final”,它的意思是对于被匿名内部类访问的局部变量,可以用final修饰,也可以不用final修饰,但必须按照有final修饰的方式使用,也就是一次赋值后,以后不能重新赋值。