|
1. 代码块
使用 {} 定义的一段代码称为代码块。
根据代码块定义的位置以及关键字,又可分为以下四种:
普通代码块(本地代码块)
定义在方法中的代码块称为普通代码块(不常用)
public void func1() {
{
System.out.println("本地代码块");
}
}
构造块(实例代码块)
实例代码块定义在类的内部,方法的外部
构造代码块一般用于初始化实例成员变量。
public class Test {
{
System.out.println("实例代码块");
}
}
静态块
使用static定义的代码块称为静态代码块
静态代码块一般用于初始化静态成员变量。
static {
System.out.println("静态代码块");
}
同步代码块(后续讲解多线程部分再谈)
1.1 代码块执行顺序
我们有这样一个代码
public class Test {
public void func1() {
{
System.out.println("本地代码块");
}
}
{
System.out.println("实例代码块");
}
static {
System.out.println("静态代码块");
}
public static void main(String[] args) {
Test test = new Test();
test.func1();
}
}
我们开始执行,输出结果会是什么?是只输出“本地代码块”吗?
不仅仅是输出了三行,顺序还和我们刚刚写的代码顺序不一样,这是为什么呢?
执行顺序
- 先执行
静态的
。[加载了类就会被执行] - 如果有多个静态的,那么看定义
顺序
。 - 如果没有实例化对象,那么只会执行静态的。[且静态的只会被执行
一次
] - 实例的数据[有多个实例,要看定义的
顺序
] - 执行构造函数
举例:(3)
我们在上面的代码的基础上,在main
函数中增加两行代码
public static void main(String[] args) {
Test test = new Test();
test.func1();
System.out.println("========");//新增加的
Test test2 = new Test();
test2.func1();
}
我们执行一下,可以看到此时静态的只被执行了一次。
# 注意事项 #
- 静态代码块不管生成多少个对象,其只会执行
一次
- 静态成员变量是类的属性,因此是在
JVM
加载类时开辟空间并初始化的 - 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的
先后次序
依次合并 - 实例代码块只有在
创建对象
时才会执行
2. 内部类
可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
我们为什么需要内部类呢?
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
public class OutClass {//外部类
class InnerClass{ //内部类
}
}
# 注意事项 #
- 定义在
class
类名{}
花括号外部的,即使是在一个文件里,都不能称为内部 - 内部类和外部类共用同一个
java
源文件,但是经过编译之后,内部类会形成单独的字节码文件
2.1 内部类的分类
内部类一般分为以下四种:
- 实力内部类
- 静态内部类
- 本地内部类(了解)
- 匿名内部类(学完接口才能知道)
2.1.1 实例内部类
class OutClass {
/**
* 实例内部类
*/
class InnerClass{
}
}
我们加上几个成员和方法
class OutClass {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
class InnerClass{
public int date4 = 4;
private int date5 = 5;
public static final int date6 = 6;
public static int date7 = 7;//报错
public InnerClass() {
System.out.println("实例内部类的构造方法");
}
public void innerFunc() {
System.out.println("实力内部类的普通方法");
}
public static void staticInnerFunc() {//报错
}
}
}
我们发现有内部类中两处他报错了。这说明#
实例内部类中不能定义静态成员变量(如果定义,需用final修饰)和静态成员方法。#
我们将这两处删掉,我们来思考一个 问题 ,如何实例化内部类?
如果我们在mian函数中写InnerClass innerClass = new InnerClass();
很显然是不对的
实例内部类是依赖于外部类对象的,所以InnerClass要被调用,必须先定义一个外部类的对象,通过外部类的对象
来调用,那类型怎么拿到呢?用外部类类型.InnerClass
public class Test {
public static void main(String[] args) {
OutClass outClass = new OutClass();
OutClass.InnerClass innerClass = outClass.new InnerClass();
//也可以写成
OutClass.InnerClass innerClass = new OutClass().new InnerClass();
}
}
我们现在在内部类InnerClass
中加入一行代码,也定义一个date1,输出结果会是什么呢?
public int date1 = 0;
运行结果展示,我们可以看到,输出内容是0,为内部类
中定义的date1。
那如果我们想要在内部类中访问外部类中的date1,应该怎么做呢?使用外部类类名.this.
public void innerFunc() {
System.out.println("实例内部类的普通方法");
System.out.println("内部类的date1:" + date1);
System.out.println("外部类的date1:" + OutClass.this.date1);
}
我们现在在内部类InnerClass
中再加入一行代码,也定义一个静态date3。
public static final int date3 = 0;
我们要怎么拿到外部的date3呢?使用外部类类名.
public void innerFunc() {
System.out.println("实例内部类的普通方法");
System.out.println("内部类的date3:" + date3);
System.out.println("外部类的date3:" + OutClass.date3);
}
# 注意事项 #
- 外部类中的
任何成员
都可以被在实例内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受
public
、private
等访问限定符的约束 - 在实例内部类方法中访问同名的成员时,
优先
访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员
来访问 - 实例内部类对象必须在
先有
外部类对象前提下才能创建 - 实例内部类的非静态方法中包含了一个指向外部类对象的
引用
- 外部类中,
不能
直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
2.1.2 静态内部类
被static
修饰的内部成员类称为静态内部类。
class OutClass {
/**
* 静态内部类
*/
static class InnerClass{
}
}
我们加上几个成员和方法
class OutClass {
public int date1 = 1;
private int date2 = 2;
public static int date3 = 3;
static class InnerClass {
public int date4 = 4;
private int date5 = 5;
public static int date6 = 6;
public InnerClass() {
System.out.println("静态内部类的构造方法");
}
public void test() {
System.out.println("执行test方法");
}
}
}
我们还是这样一个 问题 ,如何实例化内部类?
很显然如果我们在mian函数中写InnerClass innerClass = new InnerClass();
依旧是不对的。
此时我们的内部类是静态的,是不依赖外部类对象的。拿到类型依旧是用外部类类型.InnerClass
,等号右边要写成new 外部类类名.内部类构造函数
public static void main(String[] args) {
OutClass.InnerClass innerClass = new OutClass.InnerClass();
}
我们已知实例内部类可直接访问外部类的成员变量,那静态内部类是否可以呢?
我们看下图,可以看到直接访问date1
,date2
报错了,而date3
没有报错。因为 date1,date2是这个类的实例成员,静态内部类不依赖于外部类对象,也就是在使用静态内部类的时候,外部类对象都没有生成。所以#
不能在静态内部类中,直接访问外部类非静态的数据成员。#
那如果我们想要在静态内部类中访问外部类非静态成员怎么办呢?
我们只需要在内部类中提供外部类对象
就可以了。
方法1:
public void test() {
System.out.println("执行test方法");
OutClass outClass = new OutClass();
System.out.println(outClass.date1);
System.out.println(outClass.date2);
System.out.println(date3);
}
方法2:
public OutClass outClass;
public InnerClass(OutClass outClass) {
System.out.println("静态内部类的构造方法");
this.outClass = outClass;
}
public void test() {
System.out.println("执行test方法");
System.out.println(outClass.date1);
System.out.println(outClass.date2);
}
这时我们在实例化内部类时,应该给他传一个参数
public class Test {
public static void main(String[] args) {
OutClass.InnerClass innerClass = new OutClass.InnerClass(new OutClass());
}
}
# 注意事项 #
-
在静态内部类中
只能
访问外部类中的静态成员,不可
直接访问外部类中的非静态成员 -
创建静态内部类对象时,不需要先创建外部类对象
2.1.3 局部内部类
定义在外部类的方法体
或者{}
中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。
public static void func() {
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class AA {
}
}
我们也可以在局部内部类中定义方法。
public static void func() {
class AA {
public void test() {
System.out.println("AA");
}
}
// 只能在该方法体内部使用,其他位置都不能用
AA aa = new AA();
aa.test();
}
# 注意事项 #
-
局部内部类只能在所定义的方法体内部使用
-
不能被
public
、static
等修饰符修饰 -
编译器也有自己独立的字节码文件,命名格式:
外部类名字$内部类名字.class
2.1.4 匿名内部类
后序讲接口时会详细介绍。现在我们简单了解一下。
匿名对象
我们先写一个Person
类
class Preson {
public String name;
public int age;
public void func() {
System.out.println("⋌༼ •̀ ⌂ •́ ༽⋋");
}
}
这样就是一个匿名对象
:
public static void main(String[] args) {
new Person();//匿名对象
}
且匿名对象只能访问一次
。
public static void main(String[] args) {
new Person().func();
//如果再想访问name,需要再new一次
System.out.println(new Person().name);
}
匿名内部类
public static void main(String[] args) {
new Person() {
//匿名内部类,只能访问一次
public void func() {
System.out.println("⋌༼ •̀ ⌂ •́ ༽⋋");
}
}.func();
}
|
以上就是今天要讲的内容了,希望对大家有所帮助,如果有问题欢迎评论指出,会积极改正!!下一篇文章会继续讲述Java的继承的相关知识,期待大家支持,谢谢~