分类
内部类是指在一个外部类内定义的类。
- 按照是否有类名分为有名内部类和匿名内部类。
- 按照是否有static修饰分为静态内部类和非静态内部类,有static修饰的为静态内部类,没有static修饰的为非静态内部类。
1.有名内部类
Body类:
package com.jd;
/**
* 外部类
*/
public class Body {
/**
* 有名内部类
*/
public class Heart{
public void beat(){
System.out.println("正在跳动...");
}
}
}
Test类:
package com.jd;
import com.jd.Body.Heart;
public class Test {
public static void main(String[] args) {
Body body = new Body();
Heart heart = body.new Heart();
heart.beat();
}
}
说明:全局有名内部类的使用方式类似于全局变量;局部有名内部类的使用方式类似局部变量——它们都有固定的使用范围;
2. 匿名内部类
匿名内部类由于没有类名而不能单独存在,定义匿名内部类的同时须直接实例化该类
其语法格式如下:
new 父类构造器([参数列表])|接口(){
//匿名内部类类体
}
-
普通类:普通类匿名内部类实质为普通类的子类
示例:普通类: class Mammal { public void move() { System.out.println("正在移动......"); } } public class Sea { Mammal whale = new Mammal() {//调用Mammal中的无参构造方法 @Override public void move() { System.out.println("鲸鱼靠鳍移动......"); } }; }
-
抽象类:抽象类匿名内部类实质为抽象类的子类。
示例:abstract class Mammal { public Mammal(String name) { System.out.println(name); } abstract void move(); } public class Sea { Mammal mammal = new Mammal("鲸鱼") {// 调用Mammal中的有参构造方法 @Override void move() { System.out.println("鲸鱼靠鳍移动......"); } }; }
-
接口:接口匿名内部类实质为接口的实现类。
示例:interface Mammal { abstract void move(); } public class Sea { Mammal mammal = new Mammal() { @Override public void move() { System.out.println("鲸鱼靠鳍移动......"); } }; }
匿名内部类特点
-
匿名内部类一定是接口的实现类(该实现类仅能实现一个接口)或类(普通类或抽象类)的子类,其中new 关键字后面的类名或接口名即是该匿名内部类继承的父类或实现的接口;
-
匿名内部类不能自定义构造方法,但是可以通过非静态代码块初始化成员变量;
-
匿名内部类一定不能是抽象类;
-
可以在匿名内部类中添加新的属性和方法,但是这些属性和方法不能被上转型对象所调用,只能被非上转型对象方式创建的匿名内部类对象所调用,例如:
代码1: public class Test { public static void main(String[] args) { IMammal whale = new IMammal() { public void breath() { System.out.println("鲸鱼正在呼吸......"); } @Override public void move() { System.out.println("鲸鱼靠鳍游动......"); } };//此时匿名内部类对象为上转型对象 whale.breath();// 上转型对象无法调用新增的breath方法 } } interface IMammal { void move(); } 代码2: public class Test { public static void main(String[] args) { new IMammal() { public void breath() { System.out.println("鲸鱼正在呼吸......"); } @Override public void move() { System.out.println("鲸鱼靠鳍游动......"); } }.breath();// 调用新增的breath方法 } } interface IMammal { void move(); }
3. 静态内部类
静态有名内部类
Body类:
package com.jd;
/**
* 外部类
*/
public class Body {
/**
* 静态有名内部类
*/
public static class Heart{
public void beat(){
System.out.println("正在跳动...");
}
}
}
Test类:
package com.jd;
import com.jd.Body.Heart;
public class Test {
public static void main(String[] args) {
Heart heart = new Body.Heart();
heart.beat();
}
}
静态匿名内部类
class Father {
public void eat() {
System.out.println("筷子吃饭....");
}
}
/**
* 外部类
*/
public class OutClass {
/**
* 匿名内部类
*/
static Father son = new Father(){
@Override
public void eat() {
System.out.println("筷子吃饭....");
}
};
}
注意
-
如果为static内部类只能直接定义在外部类中。
public class OutClass { public static void main(String[] args) { /** * 静态有名内部类 */ static class InClass { public void printInfo() { System.out.println("我是有名内部类"); } } } }
-
静态内部类仅能直接访问外部类的静态成员变量和方法,可以通过创建外部类的对象间接使用非静态的成员变量和方法。
public class OutClass { private double weight=72; public static void print(String name) { System.out.println(name); } static class InClass{ { double weight = new OutClass().weight;//由于weight是非静态的,所以在静态内部类中使用时必须先创建外部类对象 print("Tom");//由于print方法为静态方法,所以可以直接使用。 } } }
-
在非外部类中定义的内部类和局部变量一样,其使用范围从定义的位置开始到其所在直接语句块结束。
public class OutClass { public static void main(String[] args) { if(args!=null) { class InClass{ } } InClass inClass = new InClass();//无法创建对象,因为内部类作用范围无法作用到这里 } }
-
只有有名静态内部类中才允许有静态成员(静态属性、静态代码块和静态方法)。
特点
-
内部类是一个独立的类:编译之后内部类会被编译成独立的.class文件,如果该内部类为有名内部类,则有名内部类字节码文件名为外部类的类名+$+内部类类名;如果为匿名内部类,则匿名内部类字节码文件名为外部类类名+$+数字;
-
普通外部类、抽象类和接口可以有内部类(匿名的或有名的);
-
内部类可以直接定义在类中,也可以定义在方法或代码块中;其中直接定义在外部类中的内部类可以有public、protected、默认的和private四种访问权限修饰(普通外部类、接口和抽象类只能使用public和default修饰),也可以使用static修饰( static不能修饰普通外部类、接口和抽象类);但是定义在方法或代码块中的内部类不能有访问修饰符修饰,也不能有static修饰。
-
内部类可以访问外部类的所有访问权限的成员变量,如下例:
public class OutClass { private String name; class InClass{ { System.out.println(name); } } }
-
在局部变量作用的范围内,如果定义的内部类需要使用该局部变量,则该变量必须有final修饰。
代码1 public class OutClass { public void print(final String name) {//由于局部变量name要在InClass内部类中使用,所以需要加final;方法参数前面加final修饰表示只能在调用方法时为该参数指定值,不允许再在方法体中进行二次修改。 final int weight = 9;//由于局部变量weight要在InClass内部类中使用,所以需要加final; class InClass{ public void print() { System.out.println(name+","+weight); } } new InClass().print(); } public static void main(String[] args) { new OutClass().print("Tom"); } } 代码2 public class OutClass { public static void main(String[] args) { final int i = 8;//由于局部变量i要在InClass内部类中使用,所以需要加final; class InClass { public void print() { System.out.println(i); } } InClass inClass = new InClass(); inClass.print(); } }
注意:从 Java 8开始,如果定义的内部类需要使用该局部变量,则该变量可以不使用final修饰。
Lambda 表达式
Java支持Lambda 表达式始于Java 8,它的出现简化了函数式接口匿名内部类的语法
其表达式语法如下:
([参数1], [参数2], [参数3],.... [参数n])->{代码块}
示例:
匿名内部类:
@FunctionalInterface
interface IArrayUtil{
void iterate(Object [] array);
}
public class Test {
public static void main(String args[]){
IArrayUtil arrayUtil = new IArrayUtil() {
@Override
public void iterate(Object[] array) {
for (Object obj : array) {
System.out.println(obj);
}
}
};
arrayUtil.iterate(new String[] {"小张","小王"});
}
}
Lambda 表达式:
@FunctionalInterface
interface IArrayUtil {
void iterate(Object[] array);
}
public class Test {
public static void main(String args[]) {
IArrayUtil arrayUtil = (Object[] array) -> {
for (Object obj : array) {
System.out.println(obj);
}
};
arrayUtil.iterate(new String[] { "小张", "小王"});
}
}
-
如果方法没有返回值且只有一行代码,则Lambda表达式语法可以是这种形式:
([参数1], [参数2], [参数3],… [参数n])->单行语句
如下例:
匿名内部类:
@FunctionalInterface interface IMammal { void move(String name); } public class Test { public static void main(String[] args) { IMammal whale = (name) -> { System.out.println(name+"正在移动......"); }; whale.move("鲸鱼"); } }
Lambda 表达式:
@FunctionalInterface interface IMammal { void move(String name); } public class Test { public static void main(String[] args) { IMammal whale = (name) -> System.out.println(name+"正在移动......"); whale.move("鲸鱼"); } }
-
如果方法有返回值且只有一行代码,则Lambda表达式语法可以是这种形式:
([参数1], [参数2], [参数3],.... [参数n])->表达式
如下例:
匿名内部类:
@FunctionalInterface interface IComputer { int add(int a, int b); } public class Test { public static void main(String[] args) { IComputer computer = (a, b) -> { return a+b; }; int result = computer.add(1,2); System.out.println(result); } }
Lambda 表达式:
@FunctionalInterface interface IComputer { int add(int a, int b); } public class Test { public static void main(String[] args) { IComputer computer = (a, b) -> a+b; int result = computer.add(1,2); System.out.println(result); } }