java面向对象学习--内部类
什么是内部类
还有一种类,它被定义在另一个类的内部,所以称为内部类(Nested Class)。Java的内部类分为好几种,通常情况用得不多,但也需要了解它们是如何使用的。
内部类只能依附于一个类,哪怕是实例化也要依附另外一个类
// inner class
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested"); // 实例化一个Outer
Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
inner.hello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
class Inner {
void hello() {
System.out.println("Hello, " + Outer.this.name);
}
}
}
Inner Class和普通Class相比,除了能引用Outer实例外,还有一个额外的“特权”,就是可以修改Outer Class的private字段,因为Inner Class的作用域在Outer Class内部,所以能访问Outer Class的private字段和方法。
匿名类(Anonymous Class)
// Anonymous Class
public class Main {
public static void main(String[] args) {
Outer outer = new Outer("Nested");
outer.asyncHello();
}
}
class Outer {
private String name;
Outer(String name) {
this.name = name;
}
void asyncHello() {
//这个run是定义在 java.lang.Runnable 接口中。所以Runnable是一个接口
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
}
关于Thread(r).start()的解释
这一行代码 new Thread(r).start(); 是 Java 多线程编程中最核心的启动动作。我们可以把它拆成两部分来看:
- new Thread(r) —— “雇佣一个员工并分配任务”
Thread 类:它是 Java 提供的“线程”类。你可以把它想象成一个“工人”或一个“执行引擎”。
参数 r:就是你刚才定义的那个 Runnable 对象。Runnable 的字面意思是“可运行的任务”。
组合在一起:这一步的意思是:创建一个新线程,并把我们写好的任务(r)交给它。
这时候,线程虽然创建了,但它还没动,只是处于“就绪”状态。 - .start() —— “下达开工指令”
这是最关键的一步。
当你调用 start() 方法时,Java 虚拟机会向操作系统申请:“请给我分配一个新的 CPU 线程!”
一旦申请成功,系统会立即开启一条全新的执行路径。
自动调用 run():在新线程启动后,它会自动去执行你刚才在 Runnable 里覆写的那个 run() 方法。
你可能会问,为什么不用r.run()
如果直接写 r.run();:
这只是一个普通的函数调用。代码会在当前线程(通常是主线程 main)里按顺序执行。程序会停下来等 run() 执行完了,才继续走后面的代码。这叫同步。
使用 new Thread(r).start();:
主线程(main)会瞬间执行完这一行,然后直接往下走,不管那个新线程。
与此同时,新线程会在后台独立运行 run() 里的代码。这叫异步。
这个多线程有什么用?
比如一个网页:你点了一个文件下载,这个时候其他事你都做不了,如果没有多线程的话,
还有一个例子:你的电脑都是多核的,就好比有八个灶台,你只用一个,效率就低下了
看代码
void asyncHello() {
Runnable r = new Runnable() {
@Override
public void run() {
// 假设这里有一行需要运行 5 秒钟的代码
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start(); // 开启新线程
System.out.println("主线程结束了");
}
如果没有多线程的话,就是先打出hello...然后才有主线程结束了这个,但是是不是就是有了多线程就一定是先出现主线程结束了,再出现hello呢,答案是大概率是,因为这就像一个赛跑
java8及其以上高级写法
void asyncHello() {
//这个run是定义在 java.lang.Runnable 接口中。所以Runnable是一个接口
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, " + Outer.this.name);
}
};
new Thread(r).start();
}
//对于上面的代码,有一种更高级的写法,就是 Lambda 表达式
void asyncHello() {
// 效果完全一样,不再需要显式写 run() 了,因为编译器知道 Runnable 只有这一个方法
new Thread(() -> System.out.println("Hello, " + this.name)).start();
}
/*
对于这个写法,这个就有很多讲究了:
1.为什么没有了上面的Runnable实例r了,也没有方法run了?
Thread类的构造函数,如果去看Thread的API的话,会发现她有很多重载函数
Thread() (无参)
Thread(String)
Thread(Runnable target) (最常用的,就是你用的这种)
Thread(Runnable target, String name) (还可以给线程起个名字)
你这个Thread是这样写的Thread().start()里面就只有一个东西被传入了,所以排除了第一个和第四个
然后你传入的很明显不是字符串,所以指挥使第二个,也就是说,默认你传入的就是Runnable这个接口了,因为只能传入这个接口
然后你括号里面写的是这样的() -> System.out.println("Hello, " + this.name)可以知道你覆写的就是Runnable这个接口的run方法,Runnable这个接口就一个抽象方法就是run,因为run方法本身没有任何参数所以
前面那个括号是空的就很合理了
如果这个接口的唯一的方法是void a(String words){},那就要写成(words) -> System.out.println("Hello, " + this.name)注意,不是写成String,接口里定义了 void say(String words),当你写 Lambda 的时候,编译器已经通过“对暗号”知道了这个参数一定是 String。既然大家都心知肚明,Java 就允许你省略掉类型,直接写变量名。
如果是这样就不对了,例如
interface Study {
void read();
void write();
}
然后你Thread那个括号里是这样写的,这样就错了
Study s = () -> System.out.println("Doing something...")
天知道你要覆写的是哪一个方法,只能给你算一卦了,算错了你又不开心
*/
接上文,如果一个接口有多个抽象方法,那么怎么办
1.返璞归真,直接写匿名函数的形式
Study s = new Study() {
@Override
public void read() {
System.out.println("我在读 Java 说明书");
}
@Override
public void write() {
System.out.println("我在写代码笔记");
}
};
2.default大法好
interface Study {
void read();
// 用 default 修饰,给它一个默认的动作
default void write() {
System.out.println("默认不写东西");
}
}
然后就可以用这个了
// 这个 Lambda 自动指向了唯一的抽象方法 read()
Study s = () -> System.out.println("我只读,不写");
3.哪来这么多奇技淫巧
interface Readable {
void read();
}
interface Writable {
void write();
}
顺便提一嘴,这种只有一个抽象方法的接口就是SAM接口
直接拆分成两个只有独立抽象方法的接口,然后美美用上面的写法
好像扯的太远了,我们继续讲匿名类
和内部类一样,匿名类也可以访问所在类的private字段
除了接口外,匿名类也完全可以继承自普通类。观察以下代码:
// Anonymous Class
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map1 = new HashMap<>();
HashMap<String, String> map2 = new HashMap<>() {}; // 匿名类!
HashMap<String, String> map3 = new HashMap<>() {
{
put("A", "1");
put("B", "2");
}
};
System.out.println(map3.get("A"));
}
}
静态内部类
// Static Nested Class
public class Main {
public static void main(String[] args) {
Outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}
class Outer {
private static String NAME = "OUTER";
private String name;
Outer(String name) {
this.name = name;
}
static class StaticNested {
void hello() {
System.out.println("Hello, " + Outer.NAME);
}
}
}
可以访问类的private,且不再依附于外部类

浙公网安备 33010602011771号