面向对象

Java面向对象学习的三条主线:

1.Java类的成员:属性、方法、构造器;代码块,内部类
2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)
3.关键字:this , super , static , final , abstract , interface , package , import
类:抽象的,概念上的内容。
对象:实实在在的个体。
对象是由类派生出来的,是类的实例化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* @Description
* 一、设计类,其实就是设计类的成员
* 属性 = 成员变量 = field = 域、字段
* 方法 = 成员方法 = 函数 = method
* 创建类的对象 = 类的实例化 = 实例化对象
*
* 二、类和对象的使用(面向对象思想落地的实现)
* 1、创建类,设计类的成员
* 2、创建类的对象
* 3、通过"对象.属性"或"对象.方法"调用对象的结构
*
* 三、如果创建一个类的多个对象,则每个类都独立的拥有一套类的属性。(非static)
* 修改一个对象的属性a,则不影响另外一个对象的属性a的值
* @author sweetboyZhang
* @date 2020年1月18日下午2:12:14
*/
public class PersonTest {
public static void main(String[] args) {
// 对象实例化,创建Person对象
Person p1 = new Person();

// 调用对象的结构:属性、方法
// 调用属性:"对象.属性"
p1.name = "Taylor";
p1.isMale = false;
System.out.println(p1.name);
// 调用方法:"对象.方法"
p1.eat();
p1.sleep();
p1.study("Java");

// 创建另外一个对象
Person p2 = new Person();
p2.name = "Tony";
System.out.println("p2.name: " + p2.name);// p2.name: Tony
System.out.println("p1.name: " + p1.name);// p1.name: Taylor
}
}
class Person{
// 属性
String name;
int age = 1;
boolean isMale;

// 方法
public void eat() {
System.out.println("吃饭");
}

public void sleep() {
System.out.println("睡觉");
}

public void study(String language) {
System.out.println("学习" + language);
}

}

对象的内存解析

属性(成员变量)与局部变量

相同点:
1.定义变量的格式:数据类型 变量名 = 变量值
2.先声明,后使用
3.变量都有其对应的作用域
不同点:
1.声明的位置不同
属性:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.权限修饰符不同
属性:声明时可以使用权限修饰符指明其权限
常见的权限修饰符:private , protected , public ,缺省
局部变量:不可使用权限修饰符
3.默认初始化值不同
属性:类的属性根据其类型都有默认的初始化值
基本数据类型:
整型(byte , short , int , long): 0
浮点型(float , double): 0.0
字符型(char): 0(或’\u0000’)
布尔型(boolean): false
引用数据类型(类、数组、接口):null
局部变量:没有初始化值,调用局部变量之前必须进行初始化
类中实例变量可以不用初始化,使用相应类型的默认值即可;方法中的定义的局部变量必须初始化,否则编译不通过

4.在内存中加载的位置不同
属性:加载到堆空间中(非static)
局部变量:加载到栈空间中
属性赋值顺序:1.默认初始化
2.显式初始化/代码块中赋值
3.构造器初始化
4.通过“对象.方法”或“对象.属性“初始化
JavaBean: 符合下列标准的Java类:类是公共的,一个无参的构造器,属性以及对应的get,set方法

方法

方法的声明:

1
2
3
权限修饰符 返回值类型 方法名(参数列表){
方法体
}

方法的使用:可以调用当前类的属性和方法 ,方法中不可以定义方法
方法的封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/**
* @Description
* 定义类Student,包含三个属性:学号number(int),年级state(int),成绩
score(int)。 创建20个学生对象,学号为1到20,年级(1~6)和成绩(0~100)都由随机数确定。
问题一:打印出3年级(state值为3)的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
提示:
1) 生成随机数:Math.random(),返回值类型double;
2) 四舍五入取整:Math.round(double d),返回值类型long。
* @author sweetboyZhang
* @date 2020年1月20日下午3:40:47
*/
public class StudentTest {
public static void main(String[] args) {
// 创建StudentTest对象,用来调用封装好的方法
StudentTest test = new StudentTest();
// 创建并初始化对象数组
Student[] stuArr = test.bulidStuArr(20);
// 遍历输出
test.print(stuArr);
System.out.println("*****************************");
// 找出年级为3的学生
test.searchState(stuArr, 3);
System.out.println("****************************");
// 使用冒泡排序按学生成绩排序,并遍历所有学生信息
test.bubleSort(stuArr);
test.print(stuArr);
}

/**
*
* @Description 创建并初始化对象数组
* @author sweetboyZhang
* @date 2020年1月20日下午5:15:19
* @param num
*/
public Student[] bulidStuArr(int num) {
Student[] stuArr = new Student[num];// 创建对象数组
for(int i = 0;i < num;i++) {
stuArr[i] = new Student();// 给数组元素赋值
// 给Student对象的属性赋值
stuArr[i].number = i + 1;
stuArr[i].state = (int)(Math.random()*(6-1+1)+1);// Math.random()*(b-a+1)+a
stuArr[i].score = (int)(Math.random()*(100-0+1));
}
return stuArr;
}
/**
*
* @Description 遍历并输出数组
* @author sweetboyZhang
* @date 2020年1月20日下午5:03:40
* @param stuArr
*/
public void print(Student[] stuArr) {
for(int i = 0;i < stuArr.length;i++) {
System.out.println(stuArr[i].display());
}
}

/**
*
* @Description 根据年级查找学生
* @author sweetboyZhang
* @date 2020年1月20日下午4:58:45
* @param stuArr
* @param state
*/
public void searchState(Student[] stuArr,int state){
// 遍历学生数组 打印出3年级(state值为3)的学生信息。
for(int i = 0;i < stuArr.length;i++) {
if(stuArr[i].state == state) {
System.out.println(stuArr[i].display());
}
}
}

/**
*
* @Description 使用冒泡排序按学生成绩排序
* @author sweetboyZhang
* @date 2020年1月20日下午5:02:08
* @param stuArr
*/
public void bubleSort(Student[] stuArr) {
for(int i = 0;i < stuArr.length-1;i++){
for(int j = 0;j < stuArr.length - 1 - i;j++) {
if(stuArr[j].score > stuArr[j+1].score) {
Student temp = stuArr[j];
stuArr[j] = stuArr[j+1];
stuArr[j+1] = temp;
}
}
}
}
}

class Student{
// 学号number(int),年级state(int),成绩score(int)
int number;
int state;
int score;

public String display() {
return "学号:" + number + " 年级:" + state + " 成绩:" + score;
}
}

对象数组内存解析

1
2
stus[0] = new Student();
// 引用类型变量stus[0],stus[1],stus[2]...只能存储null 或地址值 ,不能存储对象的属性。必须new一个对象用来存放该对象的属性,且这个引用类型的变量存放着指向该对象的首地址值。
1
2
// 匿名对象:创建的一个对象没有显式地赋给一个变量。只能调用一次。
new Student().display();

方法的重载:在同一个类中,允许存在一个以上的同名方法,只需参数个数或参数类型不同即可
可变个数的形参:方法名(参数的类型名 … 参数名)
方法参数的值传递机制:
形参:方法定义时,声明在小括号中的参数
实参:方法调用时,实际传递给形参的数据
方法参数传递的方式:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参(含变量的数据类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @Description 基本数据类型的参数传递测试
* @author sweetboyZhang
* @date 2020年1月21日下午6:42:36
*/
public class CanshuTest {
public static void main(String[] args) {
int x = 1;
System.out.println("修改前:x = " + x);// 1
change(x);// 在栈空间中的change()中对变量的值进行了修改,调用完成后change()中的变量出栈,并没有影响到主函数中的变量的值
System.out.println("修改后:x = " + x);// 1
}
// 声明为静态方法,便于主类直接调用,无需实例化对象
public static void change(int x) {
System.out.println("调用方法前:x = " + x);// 1
x = 2;
System.out.println("调用方法后:x = " + x);// 2
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @Description 引用类型参数传递测试
* @author sweetboyZhang
* @date 2020年1月21日下午7:11:18
*/
public class CanshuTest1 {
public static void main(String[] args) {
Person1 obj = new Person1();
obj.age = 1;
System.out.println("修改前:age = " + obj.age);// 1
change(obj);// obj指向的在堆空间中的对象的值被修改,影响了最终结果
System.out.println("修改后:age = " + obj.age);// 2
}
// 将实参中存储的地址值传递给形参,形参和实参指向堆空间中的同一个对象
public static void change(Person1 obj) {
System.out.println("调用方法前:age = " + obj.age);// 1
obj.age = 2;
System.out.println("调用方法后:age = " + obj.age);// 2
}
}

class Person1{
int age;
}

封装性

封装性的体现:
1.将类的属性私有化(private),提供公共的(public)方法来获取(get)和设置(set)属性值
2.不对外暴露的私有方法
3.单例模式
封装性的体现,需要权限修饰符的配合
Java中的4种权限修饰符(从小到大):private , 缺省 ,protected , public
权限修饰符的访问权限

4种权限均可以用来修饰类的内部结构:属性(成员变量),方法,构造器,内部类
修饰外部类:缺省,public
局部内部类,局部变量:不能被权限修饰符修饰

1.一个java文件里,public 的类只能出现一个,只能出现一个,只能出现一个,否则,不管你用哪一个类名命名文件名编译器都会报错

2.关于多态。子类继承了父类的所有成员,包括private权限的成员变量,但是继承的子类具有私有变量的拥有权但是没有使用权。

3.private的成员变量,根据权限修饰符的访问控制范围,只有在类内部才能被访问,就算是他的子类,也不能访问。

构造器(构造方法)

作用:创建对象 new + 构造器
1.若没有显示的定义类的构造器,则系统默认提供一个空参的构造器。
2.定义构造器的格式:

1
2
3
权限修饰符 类名(形参列表){

}

3.一个类中可以定义多个构造器,彼此构成重载

4.构造函数不能被继承,只能被显式或隐式的调用

this

1.this可修饰或调用:属性,方法,构造器。
2.this修饰属性和方法:this表示当前对象。
在类的方法中,使用“ this.属性 ” 或 “ this.方法 ” ,调用当前对象的属性或方法,通常可以省略。
方法的形参与类的属性重名时必须使用” this.变量 “显式调用。
3.this调用构造器:
在构造器中通过 this(参数列表) 调用本类其他构造器,不能调用自己。

package

1.更好地实现项目中类的管理
2.声明在源文件的首行。声明类或接口所属的包
3.属于标识符,遵循标识符的定义规范

import

1.在源文件中导入指定的包或接口
2.声明在包的声明和类的声明之间
3.“ xxx.* ”表示导入xxx包下的所有结构
4.如果使用的类或接口在java.lang包下定义的,则可以省略
5.使用本包下定义的类或接口,可以省略

继承性

好处:
1.减少了代码的冗余,提高了代码的复用性。
2.便于功能的扩展
3.为多态的使用提供了前提
格式:class A extends B{}
A:子类,派生类,subclass
B:父类,超类,基类,superclass
1.一旦子类A继承父类B以后,子类A中就获取了父类B中的结构中所有的属性和方法。
父类中的private属性和方法仍然被子类所继承,只是由于封装性的原因使得子类不能直接调用父类的结构而已。
2.子类继承父类后,还可以声明自己特有的属性和方法,实现功能的拓展。
子类和父类的关系不等同于集合与子集的关系。
Java中继承的规定:
1.一个类可以被多个子类继承。
2.单继承:一个类只允许有一个父类。
3.子父类的关系是相对的。(直接父类,间接父类)。
4.子类一旦继承了父类后,就获取了直接父类以及所有间接父类的属性和方法

Object类:
1.如果我们没有显式地声明一个类的父类,则此类继承于java.lang.Object类。
2.所有java类(除java.lang.Object类外)都直接或间接地继承于java.lang.Object类。
3.所有java类都具有java.lang.Object类声明的功能。
Object类中的主要方法:equal(),toString()
equals()与==的区别:
==:运算符,
1.可以使用在基本数据类型变量和引用数据类型之间(除了boolean类型);
2.如果比较的是基本数据类型的变量,比较两个变量保存的值是否相等(不一定要类型相等);
例如:2 == 2.0;’2’ == 2;’A’ ==65
3.如果比较的是引用数据类型的变量,比较两个变量存储的地址值是否相等。
equals():方法,
1.通过对象来调用,只能适用于引用数据类型的变量;
2.Object类中定义的equals()与 == 相同:

1
2
3
public boolean equals(Object obj){
rerturn (this == obj);
}

3.String , Date , File , 包装类等都重写了Object类中的equal()方法。比较的不是两个引用的地址,而是比较两 个对象的“实体内容”是否相等;
toString():
1.输出一个对象的引用时,自动调用;
2.在Object类中的定义:

1
2
3
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}// 返回类名和内存地址

3.String, Date , File , 包装类等都重写了Object类中的toString()方法。调用对象时返回的时对象的实体内 容。

方法的重写:
​子类继承父类以后,可以对父类中同名同参数方法进行覆盖操作。
​应用:重写之后,通过子类对象调用父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
​要求:
​1.子类中重写的方法名和形参列表必须与父类中被重写的方法的方法名和形参列表相同。
​2.子类中重写的方法的权限修饰符不小于父类中被重写的权限修饰符。(父类中声明为private的方法不能被重 写)
​3.返回值类型:

  • 父类中被重写的方法的返回类型为void或基本数据类型时,子类中的重写方法的返回类型也必须为void或基本数据类型。

  • 父类中被重写的方法的返回类型为A类型,子类中重写的方法的返回值类型可以是A类或A的子类。

4.子类重写的方法抛出的异常类型不大于父类被重写方法抛出的异常类型

super

1.在子类的方法或构造器中,通过“super.属性”或“super.方法”的方式 ,显示地调用父类中的属性或方法,通常省略,子类和父类出现同名的属性时不可省略。
​ 2.在子类构造器的首行显示使用“super(形参列表)”调用父类中指定的构造器,且不能与“this(形参列表)”同时存在。
​ 3.在子类构造器的首行如果没有显示地声明“super(形参列表)”或“this(形参列表)”,默认调用父类中空参的构造器。
​ 4.在子类的多个构造器中至少有一个类的构造器中使用了“super(形参列表)”来调用父类构造器。

多态性

1.对象的多态性:父类的引用指向子类对象。
​ 2.多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法。(虚拟方法调用)
​ 在编译期只能调用父类中声明的方法,在运行期实际执行的是子类重写父类的方法。(动态绑定)

1
2
Person p1 = new Student();// 父类引用指向子类对象
p1.getInfo();// 调用Student类发getInfo()方法

3.使用前提:类的继承关系,方法的重写。
4.对象的多态性只使用于方法,不使用于属性。
重载与重写的区别:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”
重写,体现了多态性,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”

instanceof

a instanceof A :判断对象a是否是类A的实例或子类。若是则返回true,否则返回false.
使用:

1
2
3
4
5
6
7
8
9
10
Person p1 = new Man();// 声明一个父类Person对象,并指向子类Man对象
// 向下转型:使用强转符()
// 向下转型目的:
//有了对象的多态性以后,内存中实际加载了子类特有的属性和方法,但是变量声明为父类类型,在编译时只能调用父类中声明的属性和方法,
//为了调用子类对象中特有的属性和方法,使用向下转型
//Women w1 = (Women)p1;// 抛出异常
if(p1 instanceof Man){
Man m1 = (Man)p1;
m1.earnMoney();// 调用子类Man中特有的方法(父类Person没有的方法)时,编译前不会报错
}

包装类

Java提供了8种基本数据类型的包装类,使得基本数据类型的变量具有类的特征
8种包装类

基本类型、包装类与String类间的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* @Description 基本类型、包装类与String类间的转换
* @author sweetboyZhang
* @date 2020年2月3日下午2:21:04
*/
public class WrapperTest {
@Test// 基本数据类型->包装类
public void test1(){
int num1 = 20;
Integer num1_Object = new Integer(num1);// 调用包装类的构造器
Integer num2_Object = 666;// 自动装箱
System.out.println(num2_Object.toString());
System.out.println(num1_Object.toString());
}

@Test// 包装类->基本数据类型
public void test2(){
Integer num3_Object = new Integer(1);
int num3 = num3_Object.intValue();// 调用xxxValue()
int num4 = num3_Object;// 自动拆箱
System.out.println(num3+1);
System.out.println(num4+1);
}

@Test // 基本数据类型,包装类->String类型
public void test3() {
// 方式一:连接运算
int num5 = 6;
String str1 = num5 + "";
System.out.println(str1);
// 方式二:调用String重载的valueOf()方法
float num6 = 6.6f;
String str2 = String.valueOf(num6);
System.out.println(str2);
}

@Test // String类型->基本数据类型,包装类
public void test4() {
// 调用 包装类的parseXxx()方法
String str3 = "1432132";
int num7 = Integer.parseInt(str3);
System.out.println(str3);
}
}

static

1.可以修饰:属性,方法,代码块,内部类。
2.使用 static 修饰属性:
静态变量只能在类主体中定义,不能在方法中定义。 静态变量属于类所有而不属于方法。
按是否使用static修饰分为静态属性(类变量)非静态属性(实例变量);
非静态属性:如果创建多个对象,每个对象都独立拥有一套类中的非静态属性。修改其中一个对象中的非静态属性时,不会导致其他对象中的相同属性发生变化。
静态属性:多个对象共享一个静态属性。当其中一个对象修改静态属性时,会导致其他对象调用此静态属性时,是修改过了的。
注意
<1>.静态变量随着类的加载而加载,可以通过”类名.静态变量”的方式进行调用。
<2>.静态变量的加载早于对象的创建。
<3>.由于类只会加载一次,则静态变量在内存中也只会存在一份(存在方法区的静态域中)。
<4>.类能调用类变量,但不能调用实例变量;对象既能调用类变量,又能调用实例变量。

类变量与实例变量内存解析

3.使用 static 修饰方法:
<1>.静态方法随着类的加载而加载,可 通过”类名.静态方法”的方式进行调用。
<2>.类可以调用静态方法,但不能调用非静态方法;对象既可以调用静态方法,又能调用非静态方法。
<3>.静态方法中只能调用静态属性或静态方法;非静态方法中既能调用静态属性又能调用静态方法。
<4>.静态方法中不能使用this,super关键字。

4.如何确定一个属性是否要声明为static
<1>.属性可以被多个对象所共享,不会随着对象的不同而不同。
<2>.类中的常量常常声明为static

5.如何确定一个方法是否要声明为static
<1>.操作静态属性的方法通常设置为static;
<2>.工具类中的方法,习惯上声明为static,例如Math,Arrays,Collections

代码块

作用:初始化类或对象
静态代码块:static{}
<1> 内部可以有输出语句
<2> 随着类的加载而执行,而且只执行一次
<3> 作用:初始化类的信息
<4> 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
<5> 静态代码块的执行优先于非静态代码块的执行
<6> 静态代码块内只能调用静态的属性和方法,不可以调用非静态的属性和方法
非静态代码块:{}
<1> 内部可以有输出语句
<2> 随着类的创建而执行
<3> 每创建一个对象,就执行一次非静态代码块
<4> 作用:可以在创建对象时,对对象的属性进行初始化
<5>非静态代码块内既能调用静态的属性和方法,又能调用非静态的属性和方法

final

1.修饰类:表明该类不能被继承。例如:String类,System类,StringBuffer类
2.修饰方法:表明该方法不能被重写,但是可以被重载。例如:Object类中的getClass()方法
3.修饰变量:此时的”变量”就称为一个常量
<1> final修饰成员变量:显式初始化,声明时直接赋值;代码块中初始化;构造器中初始化。
<2> 修饰局部变量:修饰形参时,可以不用初始化,但是在调用时必须要初始化。给常量形参赋一个实参,以后只能在方法体内使用此形参,但不能重新赋值。
修饰基本数据类型时,在赋值之后就无法改变;
修饰引用数据类型时,在赋值后指向的地址无法改变,但是对象的内容可以改变。
例如:可以对数组中的元素重新赋值,但是不能使数组指向新的地址。

static final 修饰属性:全局常量

abstract

1.abstract修饰类:抽象类
<1> 此类不能实例化
<2> 抽象类中一定有构造器,便于子类实例化调用
<3> 通过提供抽象类的子类,通过实例化子类完成相应的操作
2.abstract修饰方法:抽象方法(jdk1.8中默认权限为default)
<1> 抽象方法只有方法的声明,没有方法体
<2> 包含抽象方法的类一定是抽象类;抽象类中可以没有抽象方法
<3> 若子类重写了父类的所有抽象方法,此子类可以实例化;否则,此子类必须声明为抽象类

interface

1.在java中,接口与类是并列的两个结构。(默认权限为public)
2.接口中的成员:

  • JDK7及以前:只能定义全局常量(public static final 可以省略)和抽象方法(默认为public abstract )
  • JDK8:还可以定义静态方法(public static 只能通过接口来调用),此时被修饰的方法必须带有方法体

3.接口中不能定义构造器

4.非抽象类实现接口必须重写该接口中的所有抽象方法

5.Java通过实现多个接口,弥补单继承的局限性
class A extends B implements C,D,E{}
6.接口之间可以继承,而且可以多继承

接口和抽象类的区别是什么?

  1. 接口的方法默认是public abstract,所有方法在接口中不能有实现(Java 8 开始,default、static方法可以有默认实现);而抽象类可以有非抽象的方法(即普通成员函数),抽象方法可以被public、protected 和default 这些修饰符修饰(抽象方法就是为了被重写所以不能使用private 关键字修饰!)。
  2. 接口中除了public static final 变量,不能有其他变量;而抽象类中则不一定。
  3. 一个类可以实现多个接口;但只能实现一个抽象类。接口自己本身可以通过extends 关键字扩展多个接口。
  4. 从设计层面来说,抽象是对类本质的抽象,是一种模板设计,为了代码复用,而接口是对行为的抽象,是一种行为的规范,只约束了行为的有无,不对接口的实现做限制。

当你关注于一个事物的本质时,使用抽象类;当你关注于一个操作时,使用接口。

内部类

Java中允许一个类A声明在另一个类B中,则类A就是内部类,类B就是外部类。
1.非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员
内部类中才可声明static成员;
2.外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式;
3.成员内部类可以直接使用外部类的所有成员,包括私有的数据
4.当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的;
5.局部内部类和匿名内部类只能访问局部final变量;因为当外部类方法执行完成后,局部变量就会销毁,而内部类可能没有被销毁(只有没被引用时才被销毁)。

java中的类加载顺序:
父类静态变量/父类静态代码块->子类静态变量/子类静态代码块->父类成员变量/父类非静态代码块->父类构造器->子类成员变量/子类非静态代码块->子类构造器