定义在其他类的内部称为内部类;包含内部类的类也被称为外部类
作用:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包的其他类访问该类;
- 内部类可以直接访问外部类的私有数据,因为内部类被当成其外部类的成员。但外部类不能访问内部类的细节,如成员变量
- 匿名内部类适合用于创建那些仅需要一次使用的类,
区别:
- 内部类比外部类可以多使用三个修饰符,private,protected,static ,外部类不可以使用这三个修饰符
- 非静态内部类不能拥有静态成员
成员内部类分为静态内部类和非静态内部类
大部分时候,内部类都被作为成员内部类定义,而不是作为局部内部类。
成员内部类是一种与成员变量,方法,构造器和初始化块相似的类成员,局部内部类和匿名内部类则不是类成员。
内部类的上一级程序单元是外部类,它具有4个作用域:同一个类,同一个包,父子类,和任何位置。
package code;
public class Cow{
private double weight;
public Cow(){}
public Cow(double weight){
this.weight = weight;
}
private class CowLeg{
private double length;
private String color;
public CowLeg(){}
public CowLeg(double length,String color){
this.length = length;
this.color = color;
}
public void setLength(double length){
this.length = length;
}
public double getLength(){
return length;
}
public void setColor(String color){
this.color = color;
}
public String getColor(){
return color;
}
public void info(){
System.out.println("当前牛腿的颜色是:" + color + ",高:" + length);
System.out.println("当前这只牛的重量是" + weight);
}
}
public void test(){
CowLeg c1 = new CowLeg(11.2,"绿色");
c1.info();
}
public static void main(String [] args){
Cow cow = new Cow(44.3);
cow.test();
}
}
当前牛腿的颜色是:绿色,高:11.2
当前这只牛的重量是44.3
文件路径:生成两个class文件,一个是Cow.class,一个是Cow$CowLeg.class
当在非静态内部类的方法内访问某个变量时,系统优先在该方法内查找是否存在该名字的局部变量,接着到该方法所在的内部类中查找,再接着到该内部类所在的外部类中查找,依次查找,存在则使用该变量。
DiscernVariable.java
package code;
public class DiscernVariable{
private String prop = "外部类实例变量";
private class Inner{
private String prop = "内部类实例变量";
public void info(){
String prop = "局部变量";
System.out.println(DiscernVariable.this.prop);
System.out.println(this.prop);
System.out.println(prop);
}
}
public void test(){
Inner in = new Inner();
in.info();
}
public static void main(String []args){
DiscernVariable dv = new DiscernVariable();
dv.test();
}
}
外部类实例变量
内部类实例变量
局部变量
非静态内部类的成员可以访问外部类的private成员,但反过来不成立。
在外部类需要访问非静态内部类的成员,则必须显示创建非静态内部类对象来调用访问其实例成员。
Outer.java
package code;
public class Outer{
private int outProp = 9;
class Inner{
private int inProp = 8;
public void acessOuterProp(){
System.out.println("外部类的outProp值" + outProp);
}
}
public void accessInnerProp(){
//外部类不能直接访问非静态内部类的实例变量
//System.out.println("内部类的inProp值" + inProp);
System.out.println("内部类的inProp值" + new Inner().inProp);
}
public static void main(String [] args){
//执行下面代码,只创建了外部类对象,没有创建内部类
Outer o = new Outer();
o.accessInnerProp();
}
}
内部类的inProp值8
根据静态成员不能访问非静态成员的规则,外部类的静态方法、代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量,创建实例。
StaticTest.java
package code;
public class StaticTest{
private class In{}
public static void main(String[]args){
//静态成员(main()方法)无法访问非静态成员(In类)
//new In();
}
}
Java不允许在非静态内部类里定义静态成员。
InnerNoStatic.java
package code;
public class InnerNoStatic{
private class InnerClass{
//非静态内部类不能有静态声明
/*static{
System.out.println("========");
}
private static int intProp;
private static void test();*/
}
}
非静态内部类里不可以有静态初始化块,但可以包含普通初始化块。
如果使用static来修饰一个内部类,则这个内部类就属于外部类本身。
static关键字不可修饰外部类,但可以修饰内部类
因为外部类的上一级程序单元是包,所以不可使用static修饰,而内部类的上一级程序单元是外部类,使用static修饰可以将内部类变成外部类相关。
静态内部类可以包含静态成员,也可以包含非静态成员。
而静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
StaticInnerClassTest.java
package code;
public class StaticInnerClassTest{
private int prop1 =5;
private static int prop2 = 9;
static class StaticInnerClass{
//
private static int age;
public void accessOuterProp(){
System.out.println(prop1);
System.out.println(prop2);
}
}
}
StaticInnerClassTest.java:9: 错误: 无法从静态上下文中引用非静态 变量 prop1
System.out.println(prop1);
^
1 个错误
外部类不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象(new)作为调用者来访问静态内部类的实例成员。
AccessStaticInnerClass.java
package code;
public class AccessStaticInnerClass{
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(prop2);
//通过实例访问静态内部类的实例成员
System.out.println(new StaticInnerClass().prop2);
}
}
AccessStaticInnerClass.java:8: 错误: 找不到符号
System.out.println(prop1);
^
符号: 变量 prop1
位置: 类 AccessStaticInnerClass
AccessStaticInnerClass.java:10: 错误: 找不到符号
System.out.println(prop2);
^
符号: 变量 prop2
位置: 类 AccessStaticInnerClass
2 个错误
Java还允许在接口里定义内部类,接口里定义内部类,接口里的内部类默认使用public static 修饰,所以只能是静态内部类。
接口里可以定义内部接口,但是实际开发中很少见到
定义类的作用:
定义内部类的主要作用也是一样的
下面三种情况讨论内部类的用法:
在外部类内部使用内部类
-在外部类内部使用内部类的时候与平常使用普通类没有太大区别。唯一存在的区别是:不要在外部类的静态成员(包括静态方法和静态初始化块)中使用非静态内部类,因为静态成员不能访问非静态成员
在外部类以外使用非静态内部类
-不能使用private访问控制权限
在外部类以外的地方使用内部类,内部类完整的类名应该是OuterClass.InnerClass。有包名还要加包名前缀。
在创建非静态内部类对象之前,必须先创建其外部类对象
CreateInnerInstance.java
package code;
class Out{
class In{
public In(String msg){
System.out.println(msg);
}
}
}
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();
通过外部类实例和nei来调用内部类构造器创建非静态内部类实例
in = out.new In("测试信息");
*/
}
}
测试信息
非静态内部类的构造器必须使用外部类对象来调用。
需要在外部类以外的地方创建非静态内部类的子类,则尤其要注意:非静态内部类的构造器必须通过其外部类对象来调用。
当创建一个子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象。
SubClass.java
package code;
public class SubClass extends Out.In{
public SubClass(Out out){
out.super("hello");
}
}
非静态内部类的子类不一定是内部类,它可以是一个外部类。但非静态内部类的子类实例一样需要保留一个引用,该引用指向其父类所在外部类的对象,也就是说,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象。
3.在外部类以外使用静态内部类
因为静态内部类是外部类类相关,因此创建静态内部类对象时无需创建外部类对象,
CreateStaticInnerInstance.java
package code;
class StaticOut{
//定义一个静态内部类,不使用访问控制符
//即同一个包中的其他类可访问该内部类
static class StaticIn{
public StaticIn(){
System.out.println("静态内部类的构造器");
}
}
}
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();
*/
}
}
静态内部类的构造器
不管是静态内部类还是非静态内部类,他们声明变量的语法完全一样。
区别只是在创建内部类对象时,静态内部类只需使用外部类即可调用构造器,而非静态必须使用外部类对象来调用构造器
因为调用静态内部类的构造器时无须使用外部类对象,所以创建静态内部类的子类也比较简单,
public class StaticSubClass extends StaticOut.StaticIn(){}
当定义一个静态内部类时,其外部类非常像一个包空间
所以当程序需要使用内部类时,应该优先考虑使用静态内部类
不可以再子类中重写父类的内部类
如果把一个内部类放在方法中定义,则这个内部类就是一个局部内部类。
所有的局部成员都不能使用static修饰,
package code;
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 = 4;
System.out.println("InnerSub对象的a和b实例对象是" + is.a + "," + is.b);
}
}
InnerSub对象的a和b实例对象是5,4
局部内部类的class文件的文件名比成员内部类的class文件的文件名多了一个数字。这是为了区分同一个类里不可能有两个同名的成员内部类,而同一个类里则可能有两个以上同名的局部内部类(处于不同的方法中)。
因为局部内部类的的作用域太小了,只能在当前方法使用,所以在实际开发中很少使用局部内部类
匿名内部类适合创建只需要一次使用的类,不能重复使用。
两条规则:
AnonymousTest.java
package code;
interface Product{
public double getPrice();
public String getName();
}
public class AnonymousTest{
public void test(Product p){
System.out.println("购买了一本" + p.getName() + ",花掉了" + p.getPrice());
}
public static void main(String[] args){
AnonymousTest at = new AnonymousTest();
at.test(new Product(){
public double getPrice(){
return 4.55;
}
public String getName(){
return "英语书";
}
});
}
}
购买了一本英语书,花掉了4.55
定义匿名内部类无须class关键字,而是在定义匿名内部类时直接生成该匿名内部类的子类
匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。
当通过实现接口来创建匿名内部类时,匿名内部类也不能显式创建构造器。因此只有一个隐式的无参数构造器。所以new接口名括号里不能传入参数值。
但如果通过继承父类来创建匿名内部类,匿名内部类将拥有和父类相似的构造器,相似指的是拥有相同的形参列表。
AnonymousInner.java
package code.AnonymousInner;
abstract class Device{
private String name;
public abstract double getPrice();
public Device(){};
public Device(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class AnonymousInner{
public void test(Device d){
System.out.println("购买了一个"+ d.getName() +",花掉了" + d.getPrice());
}
public static void main(String[]args){
AnonymousInner at = new AnonymousInner();
at.test(new Device("电子示波器"){
public double getPrice(){
return 2.33;
}
});
Device d = new Device(){
{
System.out.println("匿名内部类的初始化块...");
}
public double getPrice(){
return 4.66;
}
public String getName(){
return "键盘";
}
};
at.test(d);
}
}
购买了一个电子示波器,花掉了2.33
匿名内部类的初始化块…
购买了一个键盘,花掉了4.66
Java8新功能:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰
ATest.java
package code;
interface A{
void test();
}
public class ATest{
public static void main(String [] args){
int age = 8;
A a = new A(){
public void test(){
//从Java8开始,匿名内部类,局部内部类允许访问非final的局部变量。
//在Java8以前下面语句将提示错误:age必须使用final修饰
System.out.println(age);
}
};
a.test();
}
}
8
Java8将这个功能称为“effectively final”.
原文地址:http://blog.csdn.net/u014270902/article/details/43942601