![请添加图片描述](https://img-blog.csdnimg.cn/006c2d40e8e947258b15de62f38a7684.jpeg)
内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
内部类分为四种:实例内部类,静态内部类,局部内部类,匿名内部类
其中主要讲解实例内部类和静态内部类,局部内部类不经常用稍微了解一下就行,匿名内部类涉及到抽象类和接口,在学完抽象类和接口再描述匿名内部类。
注意:内部类其实日常开发中使用并不是非常多,大家在看一些库中的代码时候可能会遇到的比较多,日常开始中使用最多的是匿名内部类。
实例内部类
成员内部类分为:
- 实例内部类(未被static修饰的成员内部类)
- 静态内部类(被static修饰的成员内部类)
成员内部类顾名思义就是类似于外部类的一个成员,在外部类中定义。
实例内部类的定义
//定义一个外部类
class OuterClass{
//外部类成员
private int data1 = 1;
int data2 = 2;
public static int data3 = 3;
public void test(){//成员方法
System.out.println("out::test");
}
//内部类
class InnerClass{
//内部类成员
public int data1 = 111;
public int data4 = 4;
int data5 = 5;
//public static int data6 = 6;
//static修饰的成员方法不能出现在非静态内部类中,
//非静态内部类类似于外部类的成员方法
//调用时才能够实例化,不调用不行
//static修饰的类成员,在类加载的时候就在方法区了
//所以静态成员与实例内部类中的成员矛盾,
//不能够出现在实例化内部类中
public static final int data6 = 6;
//加上final会使内部类中的静态成员变量变为常量
//常量是不可修改的量,所以可以在内部类中使用
//但是需要注意的一点是在非静态成员方法中不能够这样用
//这只是内部类的一个特殊语法
public void fuc(){
System.out.println("InnerClass::fuc()");
System.out.println(data1);
//此方法只能够调用内部类中的data1
//外部类中的data1如何调用呢?
//用this.data1是不可以的
//this是调用当前对象的成员
//方法1:实例化外部列,用实例化的对象调用外部类的data1
OuterClass outerClass = new OuterClass();
System.out.println(outerClass.data1);
//方法2:用外部类名调用
System.out.println(OuterClass.this.data1);
System.out.println(data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
}
}
初始化实例内部类
- 通过外部类对象实例化实例内部类
public static void main1(String[] args) {
OuterClass outerClass = new OuterClass();//实例化外部类
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
//通过外部类对象实例化
innerClass.fuc();
}
- 在一行代码同时实例化两个对象。
public static void main(String[] args) {
OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
innerClass.fuc();
}
【注意事项】
- 外部类中的任何成员都可以在实例内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
- 实例内部类对象必须在先有外部类对象前提下才能创建
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
- 实例内部类当中 不能有静态的成员变量. 非要定义,那么只能是被static final修饰的(这个只是在内部类中特有的语法,非静态成员方法不能有。)
静态内部类
被static修饰的内部成员类称为静态内部类。
静态内部类的定义
class OuterClass2{
public int data1 = 1;
int data2 = 2;
public static int data3 = 3;
public void test(){
System.out.println("out::test");
}
static class InnerClass2{
public int data4 = 4;
int data5 = 5;
public static int data6 = 6;
public void fuc(){
System.out.println("InnerClass::fuc()");
OuterClass2 outerClass2 = new OuterClass2();
System.out.println(outerClass2.data1);
//System.out.println(data1);
//System.out.println(data2);
//ERROR
//静态内部类种不能有外部类中的非静态成员的调用
//如果必须要调用就得实例化外部类对象通过对象调用
OuterClass2 outerClass2 = new OuterClass2();
System.out.println(outerClass2.data1);
System.out.println(outerClass2.data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
}
}
静态内部类的实例化
public static void main1(String[] args) {
OuterClass2.InnerClass2 innerClass2 = new OuterClass2.InnerClass2();
innerClass2.fuc();
}
【注意事项】
- 在静态内部类中只能访问外部类中的静态成员
如果想访问非静态成员就必须在内部类中实例化一个外部类对象,通过对象访问外部类成员。 - 创建静态内部类对象时,不需要先创建外部类对象
局部内部类和匿名内部类
定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。
public void func2(){
//局部内布类
class Inner{
public void test(){
System.out.println("Hello World!");
}
}
//只能在所定义的方法体内部使用,所以一般不会使用此内部类
Inner inner = new Inner();
inner.test();
}
【注意事项】
- 局部内部类只能在所定义的方法体内部使用
- 不能被public、static等修饰符修饰
- 编译器也有自己独立的字节码文件,命名格式:外部类名字$内部类名字.class
- 几乎不会使用
匿名内部类是一种特殊的内部类,它没有显式的类名,并且通常用于创建只需要使用一次的单类或接口实例。相当于一个子类实继承了父类或者接口。
class Person{
public int age = 18;
public String name = "张三";
}
public class Test{
public static void main2(String[] args) {
Person person = new Person();//创建对象访问类中成员
System.out.println(person.age);
System.out.println(person.name);
System.out.println(new Person().age);//直接实例化.成员变量
System.out.println(new Person().name);
new Person(){//匿名内部类
};
}
}
匿名内部类最常用的是实现了该接口,重写了该接口的抽象方法。
我们以student通过Arrays.sort进行实现Comparator接口来进行排序。以往我们要构造一个类实现Comparator,然后在Arrays.sort第二个参数中传入这个类的实例化。我们这个过程可以用匿名内部类来简化。
import java.util.Arrays;
import java.util.Comparator;
class Student{
public int age;
public String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) {
Student[] students = {new Student(18,"张三"),new Student(21,"李四"),new Student(15,"王五")};
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.age- o2.age;
}
});
System.out.println(Arrays.toString(students));
}
}
匿名内类通常用于创建事件处理程序、线程简单的功能它们可以访问外部类的成员变量和方法,但要注意,如果在匿名内部类中引用了外部类的局部变量,那么该量必须声明为final或者是事实上的final(即不可修改)。
匿名内部类然方便,但于没有类名,所以无法被其他代码复用。如果需要在多个地方使用相同的逻辑,建议使用具名的内部类或独立的外部类。
对象的打印
以前在对象的打印时我们会访问成员变量直接打印就像上面局部匿名内部类的例子,有时会通过在类中写一个方法进行打印。
class Person{
public int age = 18;
public String name = "张三";
public void show(){
System.out.println("姓名 " + name + " 年龄 " + age);
}
}
public class Test{
public static void main(String[] args) {
Person person = new Person();
person.show();
}
}
那么通过对象名可以直接打印吗?
class Person{
public int age = 18;
public String name = "张三";
}
public class Test{
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);
}
}
最终的输出结果为上述,其实这种输出结果就是地址经过哈希变化的值
那么打印对象名称,println方法是如何进行的呢?
所以只需要重写toString()方法就可以通过对象名来打印对象。
class Person{
public int age = 18;
public String name = "张三";
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class Test{
public static void main(String[] args) {
Person person = new Person();
System.out.println(person);
}
}
大家需要注意的是toString的名称和返回值类型还有public修饰不能改变不能改变,return返回值可以根据自己的需求改变内容