内部类
内部类就是在一个class里面定义另一个class,比如在A class 中定义一个 B class,那么B class相对于A class来说就称为内部类,而A类相对于B类就是外部类了。
为什么需要用内部类?
在一个class内部在定义另一个class可以隐藏细节和内部结构,封装性更好,让程序的结构更加合理。
内部类的分类:
1.成员内部类(非静态内部类)
2.静态内部类
3.局部内部类
4.匿名内部类
知识补充:
IDEA新建一个new object的快捷键
alt+回车两次
举例想要写语句 method1 x= new method1();
那么先手写new 类名()
比如
new method1()
然后在这一行用ALT+回车两次
就快速出来method1 x= new method1();
语句了
1.成员内部类(非静态内部类)
在一个class里定义另一个class。
举例 生成一个Outer class文件,在其中创建private int my_id,同时构建my_out method和Inner class(内部class)
然后在Inner class内部类里层在构建一个my_in method和getID method 其中getID method可以输出private int my_id
在主调用程序application中 把所有刚刚建立的method全部输出一遍。
提示!!! 在Java中,当内部类是非静态的(即没有static修饰符),它必须依赖于外部类的一个实例(也就是下面的x)才能被创建。这意味着,在创建内部类的实例之前,必须先创建一个外部类的实例。
package oop.Demo10;
public class Outer {
private int my_id=8;
public void my_out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void my_in(){
System.out.println("这是内部的方法");
}
//getID获得外部类的私有属性
public void getID(){
System.out.println(my_id);
}
}
}
package oop;
import oop.Demo10.Outer;
public class Application {
public static void main(String[] args) {
Outer x = new Outer();
x.my_out();
// 利用生成的object去生成new inner object
// 先写x.new Inner();
// 然后用快捷键alt+enter(twice)生成下面的y
Outer.Inner y = x.new Inner();
y.my_in(); //调用my_in method
y.getID(); //获得外部类
}
}
运行结果
这是外部类的方法
这是内部的方法
8
2.静态内部类
静态内部类(加了static修饰符)的构造不需要依赖于外部类对象,类中的静态组件都不需要依赖于任何对象,可以直接通过class create object,就如同创建普通实例 (比如下面的例子用的就是Outer.Inner y = new Outer.Inner();
注意如果主程序和Inner class写在一个文件里,连Outer.
都不用写)。
举例
package Class;
public class Test {
//成员变量
private String outer_name;
//成员方法
public void display(){
System.out.println("这是外部类方法!");
System.out.println(outer_name);
}
//静态内部类
public static class InnerClass{
private String inner_name;
public InnerClass() {
inner_name = "Inner Class";
}
//成员方法
public void display(){
System.out.println("这是静态内部类方法!");
System.out.println(inner_name);
}
}
// 主方法
public static void main(String[] args) {
Test x = new Test();
x.display();
// InnerClass是静态内部类,所以制造object y可以直接调用。
// 构造不依赖与外部类,可以直接通过类本身进行构造!
InnerClass y = new InnerClass();
y.display();
}
}
运行结果
这是外部类方法!
null
这是静态内部类方法!
Inner Class
思考 当Inner class标记为static时,为什么会有报错?
package oop.Demo10;
public class Outer {
private int my_id=8;
public void my_out(){
System.out.println("这是外部类的方法");
}
public static class Inner{
public void my_in(){
System.out.println("这是内部的方法");
}
//getID获得外部类的私有属性
public void getID(){
System.out.println(my_id);//注意 这里的my_id报错了,因为static部分需要先加载,而此时自定义的my_id系统还不识别,所以报错
}
}
}
思考 一个java class文件可以含有多个class块,可以同时有多个标记为public的class块吗?答案:只能有一个 public class,其他都是一般的class块,否则报错
package oop.Demo10;
import org.w3c.dom.ls.LSOutput;
public class Outer {
private int my_id=8;
public void my_out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void my_in(){
System.out.println("这是内部的方法");
}
//getID获得外部类的私有属性
public void getID(){
System.out.println(my_id);
}
}
}
public class A{//这里报错了,删掉public就正确了
int a =12;
}
- 局部内部类
在一个classA 里面的methodA里定义一个classB,classB的适用范围仅限method内部
下面是内部类InnerClass在一个方法体Test.display()中定义的示例。
package Class;
public class Test{
//成员变量
private String OuterName;
//成员方法
public void display(){
// class InnerClass就是局部内部类
class InnerClass {
public void print(){
System.out.println("666这是一个局部内部类方法!");
}
}
InnerClass y = new InnerClass();
y.print();
}
// 主方法
public static void main(String[] args) {
Test x = new Test();
//x为Test类的object
//执行x.display时发现需要先建立y,然后执行y.print
//最终获得print结果“666这是一个局部内部类方法!”
x.display();
}
}
结果:
666这是一个局部内部类方法!
Process finished with exit code 0
4.匿名内部类Anonymous Class
适用前提:必须是用在子类或者含有单一method的接口
格式:new 类名/接口名(){
重写抽象方法
};
匿名内部类:没有名字的内部类。Anonymous classes are inner classes with no name。Anonymous class内部不可以写static members,但是可以有costant members。只有两种使用范围:接口或者子类。Implement an interface(而且里面只有一个method) 或者 extends a fatherclass
使用举例:构建匿名内部子类,子类重写父类的方法
package Class;
public class Test{
public static void main(String[] args) {
//这句new Animal(){XXX};仅仅相当于创建了Animal的匿名子类对象
//如果没有写.eat();那么就是创建object完毕并,没有调用这个子类的对象,是不会有任何输出结果的
new Animal(){
//子类重写父类的方法eat
public void eat(){
System.out.println("dog is eating");
}
}.eat();
}
}
Animal class内容
package Class;
public class Animal {
public void eat(){
System.out.println("animal is eating");
};
}
输出结果:
dog is eating
Process finished with exit code 0
使用举例:匿名内部类+接口使用
在调用包含接口类型参数的method时,为了简化代码,可以直接通过匿名内部类的形式传入一个接口类型的参数,在匿名内部类中直接完成方法的实现。
package Class;
public class Test{
public static void main(String[] args) {
//下面的my_fun(XXX);是一步执行动作,执行的是后面定义的my_fun method,给了一个匿名内部类的object作为输入参数
my_fun(
new my_intef(){
@Override
public void run() {
System.out.println("666 i jump, you jump!");
}
}
);
}
//my_fun method的输入要求一个大的程序块my_intef接口的实现类对象
static void my_fun(my_intef i) {
i.run();
}
interface my_intef{
//这里定义了一个interface名叫my_intef
//内部只有一个method名叫run,符合匿名内部类的创造条件
public void run();
}
}
输出结果:
666 i jump, you jump!
Process finished with exit code 0
举例,因为不能直接创建接口interface的object (同时常规连接interface的步骤比较繁复,需要通过class继承接口,然后class内部详细完成接口里的method),我们可以通过创建interface接口的匿名内部子类class,然后创建这个子类的object来间接快速连接到接口。
package Class;
public class Test{
public static void main(String[] args) {
//下面这段new my_intef程序块就是快速创建了一个一个匿名内部类的对象,
//完成了连接接口class需要执行完毕的method go
//注意new my_intef不需要强制起个名字比如x或y(当然 也可以赋值给另一个variable),而是直接程序块书写完毕,然后程序块整体调用【.go()】;
//如果没有 【.go();】这一句,程序块书写完毕最后一个}后面是加分号的,因为这是完成一个内部类的具体书写同时新建了这个匿名内部类的object,这里注意理解
final String def_name = "Mike";
//这里的final也可以不写,但是final关键字是会被系统默热加上的
//所以这个def_name无法被再次赋值了
new my_intef(){
@Override
public void go() {
System.out.println(def_name+" said: i jump, you jump!");
}
}.go();
}
//这里定义了一个interface名叫my_intef
//内部只有一个method名叫go,符合匿名内部类的创造条件
interface my_intef{
public void go();
}
}
Mike said: i jump, you jump!
Process finished with exit code 0
思考:
以前讲过interface本身不能用来new object,同时它本体没有权限构造method。下面的例子为什么能直接new interface class类型的object?
回答:
这里并不是直接new了interface类型的object。而是新建了interface类的(匿名内部)子类的object。
这里使用了匿名内部类的创建技巧,对于这种只有单一method的接口,可以快速构建出来了一个没有名字的连接interface的子类class的object,并没有直接用interface创建object。
注意看语句的最后,}加一个分号;表示用这个(匿名内部)子类的模板建立了一个object。
然后将这个object给到了父类UserService的引用变量y。
这就是常说的,java的多态:父类的引用指向子类。
package oop.Demo10;
public class Test {
public static void main(String[] args) {
//创建了连接UserService接口的匿名子类class,并在其中完成了hello method的具体实现
//整体结构new UserService(){XXX};相当于新建了UserService的匿名子类对象
//父类型的引用UserService y接收了UserService的匿名子类对象new UserService(){XXX};
//父类的引用指向子类
UserService y= new UserService(){
@Override
public void hello() {
System.out.println("output hello");
}
};//这里有一个分号,证明这是新建了匿名内部class的object。
y.hello();
}
}
//建立了一个接口叫做UserService,里面的hello menthod需要后来对接的class去实现
//接口类
interface UserService{
void hello();
}
输出结果
output hello