内部类存在的意义
内部类可以访问该类定义所处作用域范围内的数据
内部类Inner 可以访问外部类Outer中的private变量num
public class Outer {
private int num = 10;
public class Inner {
void print() {
System.out.println(num);// 10
}
}
}
为什么内部类可以访问外部类的成员呢?
执行javac Outer.java 生成两个class文件, Outer.class和Outer$Inner.class
当外部类的对象创建了一个内部类的对象时,内部类对象必定会秘密捕获一个指向外部类对象的引用,然后访问外部类的成员时,就是用那个引用来选择外围类的成员的
内部类可以对同一包中的其他类隐藏起来
我们都知道普通的类是不能使用private和protected来修饰的,而内部类是可以用private和protected来修饰
public interface Incrementable {
void increment();
}
public class Example {
private class InnerClass implements Incrementable {
@Override
public void increment() {
System.out.println("InnerClass increment");
}
}
public Incrementable getInnerClass() {
return new InnerClass();
}
}
Example的getInnerClass返回了一个InnerClass的实例,但我们并不知道是如何实现的,而且InnerClass是private的,所以很好的实现了隐藏。
内部类可以实现 java 单继承的缺陷
Java是单根继承的,无法继承多个类,最多可以实现多个接口,但使用接口有时候有很多不方便的地方。比如我们实现一个接口就必须实现它里面的所有方法。而有了内部类就不一样了。它可以使我们的类继承多个具体类或抽象类。
public class ClassA {
public String name() {
return "Dsying";
}
public String doSomeThing() {
return "do something";
}
}
public class ClassB {
public int age() {
return 25;
}
}
public class ClassC {
private class Test1 extends ClassA {
@Override
public String name() {
return super.name();
}
}
private class Test2 extends ClassB {
@Override
public int age() {
return super.age();
}
}
public String getName() {
return new Test1().name();
}
public int getAge() {
return new Test2().age();
}
public static void main(String[] args) {
ClassC c = new ClassC();
System.out.println(c.getName());
System.out.println(c.getAge());
}
}
从上面的例子可以看出,ClassC通过内部类Test1 继承 ClassA, 内部类Test2继承ClassB, 从而继承了 Test1的name()和Test2的age()方法,并且不需要关注 ClassA的doSomeThing(), 这就是内部类实现多继承比实现多个接口的优点。
通过匿名内部类优化接口的实现
public class User {
int age;
String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
提供一个方法过滤出符合条件的User
public static List<User> filterUser(List<User> users, Predicate<User> predicate) {
return users.stream().filter(predicate).collect(Collectors.toList());
}
准备一个List<User>
List<User> users = Stream.of(new User(27, "张三"), new User(18, "李四")).collect(Collectors.toList());
通过匿名内部类实现
filterUser(users, new Predicate<User>() {
@Override
public boolean test(User user) {
return user.getName().startsWith("张");
}
})
// [User{age=27, name='张三'}]
通过lambda表达式实现
filterUser(users, user -> user.getName().startsWith("李"))
// [User{age=27, name='张三'}]
内部类的分类
内部类分为两种类型-
- 非静态内部类 -这些是类的非静态成员。
- 静态内部类 -这些是类的静态成员。
非静态内部类
类的内部类
创建内部类非常简单。您只需要在一个类中编写一个类。与类不同,内部类可以是私有的,并且一旦将内部类声明为私有,就不能从该类外部的对象访问它
class Outer_Demo {
int num;
// inner class
private class Inner_Demo {
public void print() {
System.out.println("This is an inner class");
}
}
// Accessing he inner class from the method within
void display_Inner() {
Inner_Demo inner = new Inner_Demo();
inner.print();
}
}
public class My_class {
public static void main(String args[]) {
// Instantiating the outer class
Outer_Demo outer = new Outer_Demo();
// Accessing the display_Inner() method.
outer.display_Inner();
}
}
在这里,您可以观察到Outer_Demo是外部类,Inner_Demo是内部类,display_Inner()是在其中实例化内部类的方法,并且该方法是从main方法调用的
方法的内部类
在Java中,我们可以在方法中编写类,而这将是局部类型。与局部变量一样,内部类的范围也受方法限制。只能在定义内部类的方法内实例化方法本地内部类
public class Outerclass {
// instance method of the outer class
void my_Method() {
int num = 23;
// method-local inner class
class MethodInner_Demo {
public void print() {
System.out.println("This is method inner class "+num);
}
}
// Accessing the inner class
MethodInner_Demo inner = new MethodInner_Demo();
inner.print();
}
public static void main(String args[]) {
Outerclass outer = new Outerclass();
outer.my_Method();
}
}
匿名内部类
没有类名声明的内部类称为匿名内部类。在匿名内部类的情况下,我们同时声明和实例化它们。通常,它们在需要覆盖类或接口的方法时使用。匿名内部类的语法如下
AnonymousInner an_inner = new AnonymousInner() {
public void my_method() {
// ........
// ........
}
};
以下程序显示了如何使用匿名内部类重写类的方法。
abstract class AnonymousInner {
public abstract void mymethod();
}
public class Outer_class {
public static void main(String args[]) {
AnonymousInner inner = new AnonymousInner() {
public void mymethod() {
System.out.println("This is an example of anonymous inner class");
}
};
inner.mymethod();
}
}
匿名内部类作为参数
通常,如果方法接受接口,抽象类或具体类的对象,则我们可以实现接口,扩展抽象类并将对象传递给方法。如果它是一个类,那么我们可以直接将其传递给方法
但是在这三种情况下,都可以将匿名内部类传递给该方法。这是将匿名内部类作为方法参数传递的语法:
obj.my_Method(new My_Class() {
public void Do() {
// .....
// .....
}
});
例如filterUser的参数中有一个Predicate参数为接口, 调用filterUser方法时,我可以传入一个该接口的匿名内部类
public static List<User> filterUser(List<User> users, Predicate<User> predicate) {
return users.stream().filter(predicate).collect(Collectors.toList());
}
filterUser(users, new Predicate<User>() {
@Override
public boolean test(User user) {
return user.getName().startsWith("张");
}
})
静态内部类
静态内部类是外部类的静态成员。使用其他静态成员,可以在不实例化外部类的情况下对其进行访问。就像静态成员一样,静态内部类无法访问外部类的实例变量和方法。静态嵌套类的语法如下-
class MyOuter {
static class Nested_Demo {
}
}
实例化静态嵌套类与实例化内部类有点不同。以下程序显示了如何使用静态嵌套类。
public class Outer {
static class Nested_Demo {
public void my_method() {
System.out.println("This is my nested class");
}
}
public static void main(String args[]) {
Outer.Nested_Demo nested = new Outer.Nested_Demo();
nested.my_method();
}
}