目录
1、前言
本次blog我们来共同学习以下Java当中的内部类以及代码块的知识,为今后的链表做做准备!
2、代码块
2.1 代码块概念以及分类
使用{} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
1、普通代码块
2、构造代码块
3、静态代码块
4、同步代码块(本次不进行讲解)
2.2 普通代码块
普通代码块:定义在方法中的代码块,很少有人这么用它,因为逻辑比较奇怪。
public class Main{
public static void main(String[] args) {
{ //直接使用{}定义,普通方法块
int x = 15;
System.out.println("x1 = " +x);
}
int x = 150 ;
System.out.println("x2 = " +x);
}
}
// 执行结果
x1 = 15
x2 = 150
2.3 构造代码块
- 构造代码块,也叫实例代码块:定义在类中的代码块(不加修饰符),构造代码块一般用于初始化实例成员变量。
public class codeBlock {
public String name;
public double score;
public int age;
public static String classes = "520class";
//实例化代码块
{
name = "wujianyong ";
System.out.println("实例化代码块");
}
public static void main(String[] args) {
codeBlock student1 = new codeBlock("meng",60.0,18);
System.out.println("==============================");
codeBlock student2 = new codeBlock("wujian",29.0,28);
}
}
2.4 静态代码块
- 使用static定义的代码块称为静态代码块,一般用于初始化静态成员变量。
public class codeBlock {
public String name;
public double score;
public int age;
public static String classes = "520class";
static{
classes = "520";
System.out.println("静态代码块");
}
public static void main(String[] args) {
codeBlock student1 = new codeBlock("meng",60.0,18);
System.out.println("==============================");
codeBlock student2 = new codeBlock("wujian",29.0,28);
}
}
2.5 代码执行顺序
1、当代码中有静态代码块、实例代码块、以及构造方法的时候,如果这是只创建了一个对象,那么执行顺序是:
静态代码块—>实例代码块—>构造方法
2、当代码中有静态代码块、实例代码块、以及构造方法的时候,此时创建了两个或多个对象,那么执行第二个及以后的对象的时候,就不会执行静态代码块了,静态代码块只会执行一次
student1执行:静态代码块—实例代码块—构造方法。
student2执行:实例化代码块—构造方法(不重复执行静态代码块)
3、 如果有多个静态的代码块,那就看定于的顺序,按顺序执行。 下面代码先执行classes = 520。后面改成classes = 521。
public class codeBlock {
public String name;
public double score;
public int age;
public static String classes = "520";
static{
classes = "521";
System.out.println("静态代码块");
}
static{
classes = "521";
System.out.println("静态代码块");
}
public static void main(String[] args) {
codeBlock student1 = new codeBlock("meng",60.0,18);
System.out.println("==============================");
codeBlock student2 = new codeBlock("wujian",29.0,28);
}
}
4、如果没有创建对象,也就是没有实例化对象,那么只会执行静态代码块,不会执行实例化代码块和构造代码块。
2.5.1 注意事项
- 静态代码块不管生成多少个对象,其只会执行一次;
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的;
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并;
- 实例代码块只有在创建对象时才会执行。
3、内部类
3.1 什么是内部类
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
- 在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
public class OutClass {
class InnerClass{
}
}
// OuterClass是外部类
// InnerClass是内部类
注意:
1. 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类;
public class A{
}
class B{
}
// A 和 B是两个独立的类,彼此之前没有关系
2. 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件。
3.2 内部类的分类
public class OutClass {
// 成员位置定义:未被static修饰 --->实例内部类
public class InnerClass1{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class InnerClass2{
}
public void method(){
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class InnerClass5{
}
}
}
根据内部类定义的位置不同,可分为以下几种形式:
1、局部内部类:定义才方法内部的一种类,几乎不用(这里不过多介绍);
2、实例内部类:未static修饰的成员内部类;
3、静态内部类:被static修饰的成员内部类;
4、匿名内部类
3.3 实例内部类
- 以下的实例内部类的讨论都是基于这个代码展开,其中OuterClass是外部类,InnerClass是实例内部类(没有static修饰)
class OuterClass{
public int data1 = 1;
private int data2 = 2;
public static int data3 = 3;
//外部类中的内部类
class InnerClass{
public int data1 = 11111;
public int data4 = 4;
private int data5 = 5;
public static final int data3 = 6;
/* public static int a = 10;//定义静态的变量;
public static final int data3 = 10;//加上final才能定义*/
/* public static void innerFunction(){
}*/
public void innerFunction(){
System.out.println(data1);//优先打印出内部类中的data1变量
System.out.println(this.data1);//打印出内部类中的data1变量
System.out.println("外部类的非静态的data1:"+OuterClass.this.data1);//打印出外部类中的data1变量
System.out.println("外部类的静态的data3:"+OuterClass.data3);//打印出外部类中的data3变量
System.out.println("外部类的private的data2:"+OuterClass.this.data2);//外部类中的private变量能访问到,因为都在外部类的{}中,内部类也在外部类的{}内部。
}
}
//在内部类的外部,建立一个外部类的方法,想要调用内部类的成员变量,要先创建内部类的对象,才能访问内部类中的成员变量。
public void func2(){
InnerClass inner = new InnerClass();
System.out.println(inner.data5);
}
}
public class Test {
public static void main(String[] args) {
//第一种定义对象的方法
/*OuterClass out = new OuterClass();//先建立一个外部类的对象
OuterClass.InnerClass inner =out.new InnerClass();//以这种形式建立内部类的对象,就可以实例化了*/
//第二种定义对象的方法
// OuterClass.InnerClass inner = new OuterClass().new InnerClass();
// System.out.println(inner.data1);
/* inner.innerFunction();
out.func2();*/
}
}
3.3.1 实例内部类解读
1、因为实例内部类与外部的成员变量的位置相同,所以InnerClass可以加上public/private等,或者不加都可以。
2、实例内部类中,不能定义静态的成员方法
3、在实例化内部类中,要定义静态的成员变量加final,不能直接定义静态的成员变量。
4、
(1)要实例化内部类,就是可以访问内部实例类中成员变量或方法(变量名.成员变量或者变量名.成员方法),要依赖于外部类的对象,所以要先建立一个外部类对象。
定义方式: 先建立一个外部类的对象(这里引用变量为out)
之后:外部类类名.内部类 变量名 = 外部类引用变量名.new 内部类名(),即可
OuterClass out = new OuterClass();//先建立一个外部类的对象
OuterClass.InnerClass inner =out.new InnerClass();//以这种形式建立内部类的对象,就可以实例化了
(2)也可以用以下的方法来实例化,相当于2步合成一步了,把创建Outer的对象的方法集成到一起了。inner是实例化内部类的变量名
//第二种定义对象的方法
OuterClass.InnerClass inner = new OuterClass().new InnerClass();
5、在外部类和内部类变量名相同的时候,优先访问内部类中的变量
6、如果外部类和内部类变量(非静态)同名的变量,想访问外部类OuterClass的data1的方法,通过外部类名.this.外部类的变量名。因为实例内部类中包含了外部类.this。
System.out.println(data1);//优先打印出内部类中的data1变量
System.out.println(this.data1);//打印出内部类中的data1变量
System.out.println("外部类的非静态的data1:"+OuterClass.this.data1);//打印出外部类中的data1变量
7、如果外部类和内部类静态变量同名的变量,想访问外部的静态变量,还是用外部类名.静态变量名就好了,可以不加this(补一个不加this的图)
System.out.println("外部类的静态的data3:"+OuterClass.data3);//打印出外部类中的data3变量
8、想要访问外部类中的private变量,因为这个内部类InnerClass的代码也在外部OuterClass的{}中,所以private的变量一定能访问到。
System.out.println("外部类的private的data2:"+OuterClass.this.data2);//外部类中的private变量能访问到,因为都在外部类的{}中,内部类也在外部类的{}内部。
9、在外部类中创建一个外部类方法func2(不是在内部类中),在func2中想要访问内部类的成员,必须先在func2方法中创建内部类的对象。不需要再创建外部类对象了(OuterClass)
3.4 静态内部类
- 静态内部类的讨论都是基于以下代码,其中 OuterClass是外部类,static修饰的的InnerClass是静态内部类
class OuterClass {
public int data1 = 1;
private int data2 = 2;
public static int data3 = 3;
static class InnerClass {
public int data4 = 4;
private int data5 = 5;
public static int data6 = 6;
/* public OuterClass outer ;
//用构造方法来建立对象,上面有public OuteClass outer这个对象。
public InnerClass(OuterClass outer) {
System.out.println("静态内部类的构造方法!");
this.outer = outer;
}*/
public void test1(){
System.out.println(OuterClass.data3);
// System.out.println(OuterClass.this.data1);
// System.out.println(outer.data2);
System.out.println(data4);
System.out.println(data5);
System.out.println(InnerClass.data6);
}
//在函数内部建立OuterClass的对象
public void test() {
System.out.println("test方法执行了!");
OuterClass outerClass = new OuterClass();
System.out.println(outerClass.data1);
System.out.println(outerClass.data2);
System.out.println(OuterClass.data3);
System.out.println(this.data4);
System.out.println(this.data5);
System.out.println(InnerClass.data6);
}
/*public static void test2() {
System.out.println("test2");
}*/
}
}
public class Test {
public static void main1(String[] args) {
//第一种方法
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();//实例化静态内部类
innerClass.test();
//第二种方法传参
OuterClass outer = new OuterClass();
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.test1();
}
}
3.4.1 静态内部类解读
1、如何实例化静态内部类中的成员,用外部类名.内部类名 变量名 = new 外部类名.内部类名()即可,不需要创建外部类的对象。
public static void main1(String[] args) {
//第一种方法
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();//实例化静态内部类
innerClass.test();
//第二种方法传参
OuterClass outer = new OuterClass();
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.test1();
}
2、
(1)静态内部类中只能访问外部类中的静态成员,用外部类名.变量名即可。
(2)如果非要访问外部类中的非静态成员,要在静态内部类中创建外部类的对象,或者传参,通过变量名.成员名来访问外部类中的非静态成员。
3、在静态内部类中,不能用外部类.this+变量名的形式访问外部类中的非静态成员
4、在静态类内部,访问自己类中的成员,正常访问即可,静态的变量还是用类名.变量名,不用也可以访问到。
3.4.2 静态内部类的好处
- 实例化静态内部类对象不需要创建外部类的对象;实例化实例内部类,必须先创建外部类的对象才可以。
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();//实例化静态内部类
- 注意:在静态内部类中只能直接访问外部类中的静态成员,想要访问外部类的非静态成员需要先建立外部类的对象。
3.5 局部内部类
- 定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少。
public class OutClass {
int a = 10;
public void method(){
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}
- 注意:
1. 局部内部类只能在定义的方法中使用;
2. 局部内部类不能被public、static等修饰符修饰;
3. 局部内部类编译器也有自己独立的字节码文件,命名格式:外部类名字$内部类名字.class。