嵌套类/内部类
一个类的内部又完整嵌套了另一个类的结构,被嵌套的类叫内部类 包含内部类的叫外部类,与外部类平级的叫外部其他类
内部类的特性: 可以直接访问外部类私有(private)属性
这里用的是非静态嵌套类/成员内部类/内部类举例
class Outer{//外部类
private int age = 0; //外部类私有属性
class InnerClass{//内部类
}
}
class OuterOther{//外部其他类
}
嵌套类/内部类的称呼
大致为四种,不同书籍课本有不同的描述
- 非静态嵌套类简称 内部类 /成员内部类
- 静态嵌套类 /
静态内部类(不规范) - 匿名嵌套类 / 匿名内部类
- 局部嵌套类 / 局部内部类
其中日常 用的比较多的是 非静态嵌套类/匿名嵌套类
非静态嵌套类/成员内部类/内部类
放置在外部类的成员位置上,非静态的内部类
下图中描述了内部类InnerClass与外部类DaysOfTheWeek的关系
-
内部类可以访问外部类的私有成员
-
上图中 外部类方法 中创建的内部类对象并不是 外部类的属性 而是 外部类方法的局部变量
-
只有内部类才能被四种修饰词所修饰(private/默认/protected/public)普通类只有 默认(包内可见)/ public(公有可见)
-
将内部类 私有化 只有外部类的方法才能够创建内部类的对象 (此时只有外部类能通过 方法去实例化 私有内部类 外部其他类无法访问 私有内部类)
-
该点结合下图食用 内部类可以隐藏外部类的同名字段,当内部类声明的属性 与外部类的某个属性名称 相同,此时在内部类中隐藏了外部类的属性,只能访问到内部类的那一个属性(简单来说就是 内部类某个属性名称 与 外部类的某个属性的名称 相同,那么在内部类里面就只能访问到自己的了,外部类的那一个被隐藏掉了) 若需要访问则需要通过 类名.this.属性名
(当前是非静态内部类,所以需要外部类的一个载体去访问)
-
想要使用非静态嵌套类/内部类,就必须有一个外部类作为载体。首先要新建一个外部类对象,再通过外部类对象去实例化一个内部类对象 (前提是符合访问修饰符,内部类不能是私有化的)
-
非静态嵌套类中不使用static修饰的属性或方法。
这破玩意有好多种解释的方法,反正我是被迷惑了
但是个人认为无论如何,在非静态内部类设置static修饰的方法是多余的
因为想要设置static肯定是想让某个属性/方法在 任何外部类的任何内部类中共享,想要完成这一点可以直接在外部类中添加 -
静态内部类与非静态内部类差别就是,非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类的对象,但是静态内部类却没有这个引用。
静态嵌套类/静态内部类
静态嵌套类的语法格式 是在 非静态嵌套类的基础上在类名前加static修饰符,但是使用方法有较大区别。(静态属性不用实例化对象就能访问,静态方法不能使用实例属性)
- 静态嵌套类只能方位外部类的静态属性及静态方法,不允许访问外部类的非静态字段
- 因为静态嵌套类不需要基于任何外部类的对象,可以自己独立实例化。
此时若可以访问外部类的非静态字段(由于非静态字段是属于某个对象),那么到底访问的是哪个对象呢?所以根本不存在,也就不能访问
- 因为静态嵌套类不需要基于任何外部类的对象,可以自己独立实例化。
public class InnerClassTest {
int a = 1;
static int b = 2;
private int c = 3;
private static int d = 4;
//静态嵌套类类
static class Inner2{
void test(){
System.out.println(a); //外部类属性 错误
System.out.println(b); //外部类静态属性
System.out.println(c); //外部类私有类属性 错误
System.out.println(d); //外部类静态私有属性
}
}
- 若要在 外部其他类 中创建 静态嵌套类 的对象,此时不需要 实例化 外部类 对象,直接通过类名实例化
匿名嵌套类/匿名内部类
匿名嵌套类 是一个匿名的类,并且是在类里面嵌套一个类并实例化的过程
下面是示例代码
public class test {
public static void main(String[] args) {
Toy car = new Toy() {
@Override
public void play() {
System.out.println("play car toy");
}
};//自动继承Toy类,但是这个类是匿名的,没有类名,只知道继承了Toy
car.play();//此时调用的是重写后的方法
}
}
class Toy{
public void play(){
System.out.println("play toy");
}
}
实际使用场景
匿名嵌套类 多用于调用方法时的传参 参数是一个接口类型 临时创建一个匿名嵌套类并实例化 传参
方法传参时传接口类型 在之前需要写类实现接口 然后实例化一个对象进行传参 学了匿名内部类之后,直接在传参时写一个类,实例化对象(实现接口)
直接new一个类,new超类的类名(构造参数){方法}
public class Array_ {
//注意这个mysort接口很重要,在后面实现的匿名嵌套类使用
interface mySort{
int compare(Object o1,Object o2);
}
public static void main(String[] args) {
Integer[] integers = {1,2,3,4,5,6,7,8,9,0,11,5,8};
//定制排序 (使用匿名内部类,重写父类的排序逻辑)
//定制排序重载, 预设了一个通道为下面方法的第二个参数(接口)
//第二个参数能够重写compare方法,返回值大于0小于0会影响到排序的方式
//接口编程 动态绑定 匿名内部类
//自己写的一段模拟系统的compare方法
myBubbleSort01(integers, new mySort() {
@Override
public int compare(Object o1, Object o2) {
int i1 = (Integer)o1;
int i2 = (Integer)o2;
return i1 - i2;
}
});//在这里 创建并实例了匿名内部类,该类实现mySort接口,并实现里面的方法
System.out.println(Arrays.toString(integers));
}
//这里是排序方法,传入需要排序的数组,和排序接口的实现子类的对象
public static void myBubbleSort01(Integer[] arr,mySort mysort){
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if(mysort.compare(arr[j],arr[j+1])>0)
//重点在这上方,调用接口的compare方法得到对比返回值
{
arr[j] = arr[j] ^ arr[j+1];
arr[j+1] = arr[j]^ arr[j+1];
arr[j] = arr[j] ^ arr[j+1];
}
}
}
}
}
更多关于匿名内部类的细节可以参考匿名内部类的作用
Lambda表达式
简写匿名内部类的方法,有一个大前提,该类是实现的是函数式接口,即该接口中只能有一个方法 注意,只能是接口并且只有一个方法
区分lambda表达式与语句
lambda表达式 结果回自动return返回
lambda语句 至多三行,花括号包住
若要有返回值必须写return
lambda表达式若要指定数据类型必须全部指定
public class Array_ {
interface mySort{
int compare(Object o1,Object o2);
}
public static void main(String[] args) {
Integer[] integers = {1,2,3,4,5,6,7,8,9,0,11,5,8};
myBubbleSort01(integers,(o1, o2) -> (int)o1-(int)o2);//表达式
//下面是语句!需要手动返回设置返回值!
myBubbleSort01(integers, (o1, o2) -> {
int i1 = (Integer)o1;
int i2 = (Integer)o2;
return i1 - i2;
});
}
//这里是排序方法,传入需要排序的数组,和排序接口的实现子类的对象
public static void myBubbleSort01(Integer[] arr,mySort mysort){
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
if(mysort.compare(arr[j],arr[j+1])>0)
//重点在这上方,调用接口的compare方法得到对比返回值
{
arr[j] = arr[j] ^ arr[j+1];
arr[j+1] = arr[j]^ arr[j+1];
arr[j] = arr[j] ^ arr[j+1];
}
}
}
}
}