一、面向对象和面向过程
1、面向对象思想和面向过程思想
面向过程
关注的焦点是过程:过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以把这个过程抽取为一个函数。这样就可以大大简化冗余代码,便于维护。
典型的语言:C语言
代码结构:以函数为组织单位。
是一种“执行者思维”,适合解决简单问题。扩展能力差、后期维护难度较大
面向对象
关注的焦点是类:在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,用类来表示。
典型的语言:Java、C#、C++、Python、Ruby和PHP等
代码结构:以类为组织单位。每种事物都具备自己的属性和行为/功能。
是一种“设计者思维”,适合解决复杂问题。代码扩展性强、可维护性高
2、对象、类
类:具有相同特征的事物的抽象描述,是抽象的、概念上的定义。
对象:实际存在的该类事物的每个个体,是具体的,因而也称为实例(instance)
3、面向对象完成功能的三步骤
定义类
[修饰符] class 类名{ 属性声明; 方法声明; }
创建对象
//方式1:给创建的对象命名 //把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了 类名 对象名 = new 类名(); //方式2: new 类名()//也称为匿名对象
使用"对象名.属性" 或 "对象名.方法"的方式访问对象成员(包括属性和方法)
//声明Animal类 public class Animal { //动物类 public int legs; public void eat() { System.out.println("Eating."); } public void move() { System.out.println("Move."); } }
//声明测试类 public class AnimalTest { public static void main(String args[]) { //创建对象 Animal xb = new Animal(); xb.legs = 4;//访问属性 System.out.println(xb.legs); xb.eat();//访问方法 xb.move();//访问方法 } }
匿名对象 (anonymous object)
我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
如:new Person().shout();
使用情况
如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
我们经常将匿名对象作为实参传递给一个方法调用
二、对象的内存解析
HotSpot Java虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分(Runtime Data Area)
其中:
堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
栈(Stack):是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。
方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
class Person { //类:人 String name; int age; boolean isMale; } public class PersonTest { //测试类 public static void main(String[] args) { Person p1 = new Person(); p1.name = "赵同学"; p1.age = 20; p1.isMale = true; Person p2 = new Person(); p2.age = 10; Person p3 = p1; p3.name = "郭同学"; } }
说明:
堆:凡是new出来的结构(对象、数组)都放在堆空间中。
对象的属性存放在堆空间中。
创建一个类的多个对象(比如p1、p2),则每个对象都拥有当前类的一套"副本"(即属性)。当通过一个对象修改其属性时,不会影响其它对象此属性的值。
当声明一个新的变量使用现有的对象进行赋值时(比如p3 = p1),此时并没有在堆空间中创建新的对象。而是两个变量共同指向了堆空间中同一个对象。当通过一个对象修改属性时,会影响另外一个对象对此属性的调用。
四、成员变量(field)
1、如何申明成员变量
[修饰符1] class 类名{ [修饰符2] 数据类型 成员变量名 [= 初始化值]; }
位置要求
必须在类中,方法外
修饰符
常用的权限修饰符有:private、缺省、protected、public
其他修饰符:static、final
数据类型
任何基本数据类型(如int、Boolean) 或 任何引用数据类型。
成员变量名
属于标识符,符合命名规则和规范即可。
初始化值
根据情况,可以显式赋值;也可以不赋值,使用默认值
2、成员变量 vs 局部变量
2.1、变量分类
在方法体外,类体内声明的变量称为成员变量。
在方法体内部等位置声明的变量称为局部变量。
其中,static可以将成员变量分为两大类,静态变量和非静态变量。其中静态变量又称为类变量,非静态变量又称为实例变量或者属性。接下来先学习实例变量。
2.2、对比
相同点
- 变量声明的格式相同: 数据类型 变量名 = 初始化值
- 变量必须先声明、后初始化、再使用
- 变量都有其对应的作用域。只在其作用域内是有效的
不同点
1、声明位置和方式
(1)实例变量:在类中方法外
(2)局部变量:在方法体{}中或方法的形参列表、代码块中
2、在内存中存储的位置不同
(1)实例变量:堆
(2)局部变量:栈
3、生命周期
(1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消亡,
而且每一个对象的实例变量是独立的。
(2)局部变量:和方法调用的生命周期一样,每一次方法被调用而在存在,随着方法执行的结束而消亡,
而且每一次方法调用都是独立。
4、作用域
(1)实例变量:通过对象就可以使用,本类中直接调用,其他类中“对象.实例变量”
(2)局部变量:出了作用域就不能使用
5、修饰符(后面来讲)
(1)实例变量:public,protected,private,final,volatile,transient等
(2)局部变量:final
6、默认值
(1)实例变量:有默认值
(2)局部变量:没有,必须手动初始化。其中的形参比较特殊,靠实参给它初始化。
3、对象属性的默认初始化赋值
五、成员方法
1、定义
方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程
2、声明方法
[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{ 方法体的功能代码 }
a.修饰符:可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会一一学习。其中,权限修饰符有public、protected、private。在讲封装性之前,我们先默认使用pulbic修饰方法。
其中,根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方法,非静态方法又称为实例方法。咱们在讲static前先学习实例方法。
b.返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。
无返回值,则声明:void
有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“return 返回值”搭配使用
c.方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”
d.形参列表:表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。
无论是否有参数,()不能省略
如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:
一个参数: (数据类型 参数名)
二个参数: (数据类型1 参数1, 数据类型2 参数2)
参数的类型可以是基本数据类型、引用数据类型
e.throws 异常列表:可选
f.方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码
g.关于方法体中return语句的说明:
return语句的作用是结束方法的执行,并将方法的结果返回去
如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。
return语句后面就不能再写其他代码了,否则会报错:Unreachable code
3、如何嗲用
对象.方法名([实参列表])
4、方法重载
方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
参数列表不同,意味着参数个数或参数类型的不同
重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
重载方法调用:JVM通过方法的参数列表,调用匹配的方法。
先找个数、类型最匹配的
再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错
//返回两个整数的和 public int add(int x,int y){ return x+y; } //返回三个整数的和 public int add(int x,int y,int z){ return x+y+z; } //返回两个小数的和 public double add(double x,double y){ return x+y; }
5、可变个数的形参
在**JDK 5.0 中提供了Varargs(variable number of arguments)**机制。即当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。
方法名(参数的类型名 ...参数名)
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量 public static void test(int a ,String[] books); //JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量 public static void test(int a ,String...books);
特点:
可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
可变个数形参的方法与同名的方法之间,彼此构成重载
可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。
方法的参数部分有可变形参,需要放在形参声明的最后
在一个方法的形参中,最多只能声明一个可变个数的形参
案列:如下方法构成形参
public class MathTools { //求两个整数的最大值 public int max(int a,int b){ return a>b?a:b; } //求两个小数的最大值 public double max(double a, double b){ return a>b?a:b; } //求三个整数的最大值 public int max(int a, int b, int c){ return max(max(a,b),c); } //求n个整数的最大值 public int max(int... nums){ int max = nums[0];//如果没有传入整数,或者传入null,这句代码会报异常 for (int i = 1; i < nums.length; i++) { if(nums[i] > max){ max = nums[i]; } } return max; } /* //求n整数的最大值 public int max(int[] nums){ //编译就报错,与(int... nums)无法区分 int max = nums[0];//如果没有传入整数,或者传入null,这句代码会报异常 for (int i = 1; i < nums.length; i++) { if(nums[i] > max){ max = nums[i]; } } return max; }*/ /* //求n整数的最大值 public int max(int first, int... nums){ //当前类不报错,但是调用时会引起多个方法同时匹配 int max = first; for (int i = 0; i < nums.length; i++) { if(nums[i] > max){ max = nums[i]; } } return max; }*/ }
6、方法的参数传递机制
1、形参和实参
形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。
实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
六、关键字:package、import
1、package
package,称为包,用于指明该文件中定义的类、接口等结构所在的包
说明:
一个源文件只能有一个声明包的package语句
package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意
包通常使用所在公司域名的倒置:com.atguigu.xxx。
大家取包名时不要使用"java.xx"包
包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就表示一层文件目录。
同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下可以定义同名的结构(类、接口)
包的作用
包可以包含类和子包,划分项目层次,便于管理
帮助管理大型软件系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
解决类命名冲突的问题
控制访问权限
jdk中的包介绍
java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能
java.net----包含执行与网络相关的操作的类和接口。
java.io ----包含能提供多种输入/输出功能的类。
java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
java.text----包含了一些java格式化相关的类
java.sql----包含了java进行JDBC数据库编程的相关类/接口
java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
2、import
为了使用定义在其它包中的Java类,需用import语句来显式引入指定包下所需要的类。相当于import语句告诉编译器到哪里去寻找这个类
注意
import语句,声明在包的声明和类的声明之间。
如果需要导入多个类或接口,那么就并列显式多个import语句即可
如果使用a.*导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入。
如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。
(了解)import static组合的使用:调用指定类或接口下的静态的属性或方法
封装,继承,多态
封装:类的私有化,使用访问权限控制符privte、default、protected、public实现封装
继承:当几个类对象中有共同的属性和方法时,就可以把这些属性和方法抽象并提取到一个基类中,每个类对象特有的属性和方法还是在本类对象中定义,这样,只需要让每个类对象都继承这个基类,就可以访问基类中的属性和方法了。继承基类的每个类对象被称为派生类。基类也被称为父类或超类,派生类也被称为子类
多态:所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定,父类引用指向子类对象,即子类对象赋值给父类运行时表现的是子类的特征,Animal a = new Dog();a.eat()//运行时还是dog对象类本身的eat方法
this
为什么要使用this关键字?
当在方法中出现了局部变量和成员变量同名的时候,那么在方法中怎么区别局部变量成员变量呢?可以在成员变量名前面加上this.来区别成员变量和局部变量
如果没有命名冲突,可以省略this
。例如:
class Person { private String name; public String getName() { return name; // 相当于this.name } }
但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this
:
class Person { private String name; public void setName(String name) { this.name = name; // 前面的this不可少,少了就变成局部变量name了 } }
static
当在定义类的时候,类中都会有相应的属性和方法。而属性和方法都是通过创建本类对象调用的。当在调用对象的某个方法时,这个方法没有访问到对象的特有数据时,方法创建这个对象有些多余。可是不创建对象,方法又调用不了,这时就会想,那么我们能不能不创建对象,就可以调用方法呢?
可以的,我们可以通过static关键字来实现。static它是静态修饰符,一般用来修饰类中的成员
特点:
1. 被static修饰的成员变量属于类,不属于这个类的某个对象。(也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量)
2. static修饰的变量和方法可以通过类名直接调用
Super
Super是直接父类对象的引用,可以访问父类中被子类覆盖的方法或属性。
普通方法中: 没有顺序限制,可以直接调用
构造方法中:第一行如果没有super()作为父类的初始化函数,那么java会自动默认super,所以构造中加不加无所谓:
final
final关键字代表最终,不可改变的.
常见有5种用法,我们来归纳总结一下:
1. 用来修饰一个类
2. 用来修饰一个方法
3. 用来修饰成员变量
4. 用来修饰局部变量
5. 用来修饰方法参数
修饰类
如果声明一个类为final类, 那么这个类就是最终类,不能被继承,当写一个类去继承它的时候
注意:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写.
修饰方法
public class ParentClass {
public void happy() {
System.out.println("高兴");
}
//用final关键字修饰的方法
protected final void play(){
System.out.println("开心的玩耍");
}
}
我们在继承类中去看下 play()方法是否可以被override
修饰成员变量
在java中,成员变量一般都有默认值, 比如 int 型变量,默认值为0 boolean型变量默认值为false
String型变量,默认值为null
我们来看上面的例子, 3个同样类型的 int型 成员变量, number temp 默认值都是0 , 写和没写都没有报错. 但是加上final之后的num变量,没有初始化就报错.
总结一: 由于成员变量具有默认值,但是用了final关键字修饰之后必须要手动赋值初始化值.
我们在看一个例子
总结二: 对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可被改变。
final修饰局部变量
对于基本类型来说,不可变说的是变量当中的数据不可改变;
请看如下例子:
对于引用类型来说,不可变指的是变量当中的地址值不可改变
我们再来看下个例子,
Student为一个普通的javabean类
public class Student { private String name; public Student() { } public Student(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
第一种情况,没有final修饰的测试类
public class FinalVarTest { public static void main(String[] args) { Student student1 = new Student("勒布朗.詹姆斯"); System.out.println(student1); //打印内存地址 System.out.println(student1.getName()); student1 = new Student("斯蒂芬.库里"); System.out.println(student1); //打印内存地址 System.out.println(student1.getName()); } }
结果
com.finaltest.Student@4aa298b7 //内存地址
勒布朗.詹姆斯
com.finaltest.Student@7d4991ad //内存地址
斯蒂芬.库里
输出结果:内存地址不同
第二种情况: 使用final修饰测试类
输出结果:内存地址不同
若用final修饰,地址值不可以被改变,否则直接报错.
地址值虽然不能变,但是内容可以变(地址所指向的对象的属性可以被该变)。
public class FinalVarTest { public static void main(String[] args) { //使用final修饰的对象 final Student student2 = new Student("詹姆斯.哈登"); System.out.println(student2); //打印内存地址 System.out.println(student2.getName()); student2.setName("安东尼.戴维斯"); System.out.println(student2); //打印内存地址 System.out.println(student2.getName()); } }
结果
com.finaltest.Student@4aa298b7
詹姆斯.哈登
com.finaltest.Student@4aa298b7
安东尼.戴维斯
两者的内存地址是相同的,但是名字(属性)是可以修改的
final修饰参数
在方法参数前面加final关键字就是为了防止数据在方法体中被修改。
主要分两种情况:第一,用final修饰基本数据类型;第二,用final修饰引用类型。
第一种情况,修饰基本类型(非引用类型)。这时参数的值在方法体内是不能被修改的,即不能被重新赋值。否则编译就通不过。例如
第二种情况,修饰引用类型。这时参数变量所引用的对象是不能被改变的。作为引用的拷贝,参数在方法体里面不能再引用新的对象。否则编译通不过。例如
但是对于引用,如果我是这样(也就是引用对象的地址不能改变,但是对象中的内容是可以改变的),则不会报任何错,完全能编译通过。
静态代码块、代码块、构造方法(构造器)
1、构造方法
构造方法就是没有返回值类型,且方法名与类名相同的方法,在对象创建时候执行
2、构造代码块:
类中或者方法中使用{}定义的一段代码,对象创建时执行,如果构造方法与构造代码块同时存在,那么代码块会在构造方法前面执行
- 局部代码块:直接定义在方法内部的代码块,比如if while for的结合使用
- 初始化构造代码块:直接定义在类中(运行时侯还是方法构造器中)
3、静态代码块:
静态代码块就是在构造代码块前面加上static方法,在只有第一次加载字节码时候执行,之后不再执行
普通类 抽象类 接口使用场景
将一组有具体属性和行为的实体可以定义为一个普通类,虚拟的属性和行为定义为抽象类作为其他类的基类
接口:没有类可言 只是定义一组行为方法用接口,接口本质就是锲约、标准、规范就像我们法律一样,制定好之后大家要遵守,java8在接口中可以定义默认方法和静态方法
1.为什么使用抽象类与接口
多个类之间存在共同的功能,比如各种车都会跑,但是跑的方式不同,可以把这个跑的动作抽象为一个抽象的方法,
接口是更加纯粹的抽象类 少了成员变量和构造器,子类如果需要继承父类成员变量或者控制子类实例化时用抽象类,否则用接口
抽象类:不能实例化但是有构造器,Java中的抽象类可以有构造函数吗?/抽象类中的构造方法作用是什么?_KHan001的博客-CSDN博客_抽象类的构造方法有什么用,成员变量 普通方法 构造器 抽象方法
接口:静态常量 抽象方法 默认方法 私有方法(java9+) 静态方法
内部类
什么是内部类
除了平时定义的class类,还有一种类,它被定义在另一个类的内部,所以称为内部类(Nested Class)。Java的内部类分为Inner Class、Anonymous Class和Static Nested Class三种,通常情况用得不多,但也需要了解它们是如何使用的。
Inner Class、Anonymous Class和Static Nested Class
Inner Class
如果一个类定义在另一个类的内部,这个类就是Inner Class:
class Outer { class Inner { // 定义了一个Inner Class } }
上述定义的Outer
是一个普通类,而Inner
是一个Inner Class,它与普通类有个最大的不同,就是Inner Class的实例不能单独存在,必须依附于一个Outer 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
,我们必须首先创建一个Outer
的实例,然后,调用Outer
实例的new
来创建Inner
实例:
Outer.Inner inner = outer.new Inner();
这是因为Inner Class除了有一个this
指向它自己,还隐含地持有一个Outer Class实例,可以用Outer.this
访问这个实例。所以,实例化一个Inner Class不能脱离Outer实例。
Inner Class和普通Class相比,除了能引用Outer实例外,还有一个额外的“特权”,就是可以修改Outer Class的private
字段,因为Inner Class的作用域在Outer Class内部,所以能访问Outer Class的private
字段和方法。
观察Java编译器编译后的.class
文件可以发现,Outer
类被编译为Outer.class
,而Inner
类被编译为Outer$Inner.class
。
Anonymous Class
它不需要在Outer Class中明确地定义这个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() { Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello, " + Outer.this.name); } }; new Thread(r).start(); } }
观察asyncHello()
方法,我们在方法内部实例化了一个Runnable
。Runnable
本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了Runnable
接口的匿名类,并且通过new
实例化该匿名类,然后转型为Runnable
。在定义匿名类的时候就必须实例化它,定义匿名类的写法如下:
Runnable r = new Runnable() { // 实现必要的抽象方法... };
匿名类和Inner Class一样,可以访问Outer Class的private
字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心类名,比直接定义Inner Class可以少写很多代码。
Static Nested Class
最后一种内部类和Inner Class类似,但是使用static
修饰,称为静态内部类(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); } } }
用static
修饰的内部类和Inner Class有很大的不同,它不再依附于Outer
的实例,而是一个完全独立的类,因此无法引用Outer.this
,但它可以访问Outer
的private
静态字段和静态方法。如果把StaticNested
移到Outer
之外,就失去了访问private
的权限。
什么时候使用内部类
在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。
public class TestController {
//静态内部类
static class MyThread extends Thread{
int num = 1000;
@Override
public void run() {
for (int i = 0; i < num; i++) {
System.out.println(String.format("当前线程%s输出%d",currentThread().getName(),i));
}
}
}
public static void main(String[] args) {
new MyThread().start();
for (int i = 0; i < 10000; i++) {
System.out.println(String.format("当前线程%s输出%d","main",i));
}
}
}
小结
Java的内部类可分为Inner Class、Anonymous Class和Static Nested Class三种:
- Inner Class和Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有
Outer.this
实例,并拥有Outer Class的private
访问权限; - Static Nested Class是独立类,但拥有Outer Class的
private
访问权限。