Java面向对象总结
面向对象基本概念
java 是面向对象的编程语言,对象就是面向对象程序设计的核心。所谓对象就是真实世界中的实体,对象与实体是一一对应的,也就是说现实世界中每一个实体都是一个对象,它是一种具体的概念。
对象有以下特点:
- 对象具有属性和行为。
- 对象具有变化的状态。
- 对象具有唯一性。
- 对象都是某个类别的实例。
- 一切皆为对象,现实世界中的所有事物都可以视为对象(万物皆对象)。
面向对象的三大核心特性
1、 可管理性:能够将功能与数据结合,方便管理。
2.、可扩展性:它使面向对象设计脱离了基于模块的设计,便于软件的修改。
3.、可重用性:它是面向对象软件开发的核心思路,提高了开发效率。面向对象程序设计的抽象、继承、封装和多态四大特点都围绕这个核心。
类和对象
类:类是一个模板、一种分类,它描述一类对象的行为(方法)和状态(属性)。
对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
类和对象的定义格式
类的定义:
[public] class 类名称{ 属性名称; 返回值类型 方法名称(){} } //例: class student{ private string name; private int age; public void teal(){ system.out.println(name+"今年"+age+"岁!"); } }
对象的定义:
一个类要想真正的进行操作,则必须依靠对象,对象的定义格式如下:
类名称 对象名称 = new 类名称(); 例:student stu = new student();
如果要想访问类中的属性或方法(方法的定义),则可以依靠以下的语法形式:
访问类中的属性:对象.属性 ;
调用类中的方法:对象.方法();
对象的创建
对象是根据类创建的。在java中,使用关键字new来创建一个新的对象。创建对象需要以下三步:
声明:声明一个对象,包括对象名称和对象类型。
实例化:使用关键字new来创建一个对象。
初始化:使用new创建对象时,会调用构造方法初始化对象。
例子:
public dog(string name){ //这个构造器仅有一个参数:name system.out.println("小狗的名字是 : " + name ); } public static void main(string[] args){ // 下面的语句将创建一个puppy对象 dog dog = new dog( "tom" ); } }
封装
封装的概念:
封装是面向对象思想的三大特征之一,封装就是隐藏实现细节,仅对外提供访问接口。实现细节部份包装、隐藏起来的方法。
封装的好处:
减少耦合性、隐藏信息、代码复用、安全性高、类内部的结构可以*修改 ... 。
封装的缺点:会影响执行效率
封装之前:属性都可以直接访问和修改。
class person{ string name; int age; }
封装之后:
属性都不可以直接访问和修改,需通过set和get方法进行访问和修改。
class person{ //属性是成员变量,私有化属性,使得其他对象不能直接访问属性 private string name; private int age; //参数及方法内定义的变量是局部变量 public void setname(string name){ this.name = name; } public string getname(){ return name; } }
构造方法
构造方法是类的一种特殊方法,用来初始化类的一个新的对象。java 中的每个类都有一个默认的构造方法,它必须具有和类名相同的名称,而且没有返回类型。构造方法的默认返回类型就是对象类型本身,并且构造方法不能被static、final、synchronized、abstract 和 native 修饰。
无参构造方法:
如果一个类没有定义构造方法,则默认无参构造,如果有定义有参构造,最好再显示定义一个无参构造方法
public student(){ // 默认无参构造方法 }
带参构造方法:
public student(string name){ this.name = name; }
多参构造方法:
public student(string name,int age){ this.name = name; this.age = age; //... }
总结:
(1)构造方法名称与类名相同,没有返回值声明(包括 void)
(2)构造方法用于初始化数据(属性)。
(3)每一个类中都会有一个默认的无参的构造方法。
(4)如果类中有显示的构造方法,那么默认构造方法将无效。
(5)如果有显示的构造方法,还想保留默认构造方法,需要显示的写出来。
(6)构造方法可以有多个,但参数不一样,称为构造方法的重载。
(7)在构造方法中调用另一个构造方法,使用this(...),该句代码必须在第一句。
(8)构造方法之间的调用,必须要有出口。
(9)给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留。
(10)一个好的编程习惯是要保留默认的构造方法。(为了方便一些框架代码使用反射来创建对象)
this关键字
this关键字指向的是当前对象的引用。
调用类中的属性:this.属性名称,指的是访问类中的成员变量,用来区分成员变量和局部变量(重名问题)。
调用类中的方法:this.方法名称,用来访问本类的成员方法。
调用类构造方法:this(); 访问本类的构造方法,()中可以有参数,如果有参数,就是调用指定的有参构造。
public num_min(int num1, int num2) { this.num1 = num1; this.num2 = num2; } public num_min(int num1, int num2, int num3) { this(num1,num2); // 调用类构造方法 /*this.num1 = num1; this.num2 = num2;*/ this.num3 = num3; }
注意:
1、this() 不能使用在普通方法中,只能写在构造方法中。
2、this(实参列表)必须放在构造方法中的第一行。
3、在构造方法中,this(实参列表)不可递归(自己调用自己)。
值传递与引用传递
在java中只有值传递,并没有所谓的引用传递
java数据类型可以分为两大类:
基本类型(primitive types)和引用类型(reference types)
java基本数据类型的大小、范围、默认值
数据类型 | 描述 | 占用字节 | 取值范围 | 默认值 |
---|---|---|---|---|
byte | java中最小的数据类型 | 1个字节 | -128 ~ 127 | 0 |
short | 短整型 | 2个字节 | -32768 ~ 32717 | 0 |
int | 整型,用于存储整数 | 4个字节 | -2147483648 ~ 2147483647 | 0 |
long | 长整型 | 8个字节 | -2^63 ~ 2^63-1 | 0l |
float | 浮点型,用于存储带小数点的数字 | 4个字节 | / | 0.0f |
double | 双精度浮点型,用于存储带有小数点的数字 | 8个字节 | / | 0.0d |
char | 字符型,用于存储单个字符 | 2个字节 | 0 ~ 65535 | \u0000(空格) |
boolean | 布尔类型,用于判断真或假 | 1个字节 | 仅有两个值,即true、false | false |
static关键字
static关键字的作用:方便在没有创建对象的情况下来进行调用(方法/变量)。
a、使用static关键字修饰一个属性:声明为static的变量实质上就是全局变量。
b、使用static关键字修饰一个方法:在一个类中定义一个方法为static,无需本类的对象即可调用此方法(类调用)。
c、使用static关键字修饰一个类(内部类):
声明为static的方法有以下几条限制:
它们仅能调用其他的static 方法,反过来是可以的,
它们只能访问static数据,
它们不能以任何方式引用this或super,
不允许用来修饰局部变量。
总结:被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
可以参考:
继承
继承是面向对象三大特征之一
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
被继承的类称为父类(超类),继承父类的类称为子类(派生类),通过继承可以实现代码重用
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
构造器而言,它只能够被调用,而不能被继承,可以通过使用super()进行调用,
对于继承而已,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器(通过super()),而且必须是在子类构造器中做的第一件事(第一行代码)。
对于protected而言,它指明就类用户而言,他是private,但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的
java 的继承是单继承,不允许多继承,但是可以多重继承(继承链)
语法:
[访问权限] class 子类名 extends 父类名{ //类体定义; }
示例:
public class dog{ private string name; private string sex; public void eat(){ system.out.println("吃饭"); } } public class homedog extends dog{ //类的定义 } public class huskydog extends dog{ //类的定义 }
继承的好处:
a、提高代码的复用性
b、提高代码的维护性
c、让类与类之间产生关系,是多态的前提
继承的缺点:
提高了类与类之间的耦合性
对象初始化
一、代码块的概念
静态代码块、构造代码块、局部代码块
静态代码块:在类中,方法体和代码块之外(全局作用域),使用static修饰的代码块,我们称之为静态代码块。(可以定义任意多个,顺序执行)
静态代码块随着类的第一次加载而执行,且只执行一次。
在静态代码块中,不能直接操作类中的成员变量,但是可以操作当前类中的成员变量。
静态代码块要想操作静态变量,静态变量必须在静态代码块之前声明,因为再类中它们的执行顺序是从上而下的。
构造代码块:在类中,方法体和代码块之外(全局作用域),没有使用static修饰的代码块,我们称之为静态代码块。(可以定义任意多个,顺序执行)
构造代码块随着对象的创建而执行,对象创建多少次构造代码块就执行多少次。
构造代码块优先于构造方法执行,创建对象时先执行当前类中的构造代码块,然后再执行构造方法。
局部代码块:在局部中定义的代码块。(可以定义任意多个,顺序执行)
执行顺序:
静态代码块 > 构造代码块 > 构造方法
class test{ public string str1; //普通字段 public static string str2; // 静态字段 static{ //静态代码块 } { //构造代码块 } public test() { //构造函数 } public static void main(string[] args) { { system.out.println("局部代码块"); } } }
二、 创建子类对象时,对象的初始化顺序
1、字段初始化、代码块和构造函数的执行顺序
public class codeblocktest { public static void main(string[] args) { child child = new child(); } } class father { public static string fatherstr1 = "fatherstr1(静态字段初始化值)"; public string fatherstr2 = "fatherstr2(字段初始化值)"; static { system.out.println("父类静态代码块:" + fatherstr1); fatherstr1 = "fatherstr1(静态代码块赋值)"; } { system.out.println("父类构造代码块:" + fatherstr2); fatherstr2 = "fatherstr2(构造代码块赋值)"; } public father() { system.out.println("父类构造函数块:" + fatherstr2); fatherstr2 = "fatherstr2(构造函数赋值)"; } } class child extends father { public static string childstr1 = "childstr1(静态字段初始化值)"; public string childstr2 = "childstr2(字段初始化值)"; static { system.out.println("子类静态代码块:" + childstr1); childstr1 = "childstr1(静态代码块赋值)"; } { system.out.println("子类构造代码块:" + childstr2); childstr2 = "childstr2(构造代码块赋值)"; } public child() { system.out.println("子类构造函数:" + childstr2); childstr2 = "childstr2(构造函数赋值)"; } }
输出结果:
父类静态代码块:fatherstr1(静态字段初始化值) 子类静态代码块:childstr1(静态字段初始化值) 父类构造代码块:fatherstr2(字段初始化值) 父类构造函数块:fatherstr2(构造代码块赋值) 子类构造代码块:childstr2(字段初始化值) 子类构造函数:childstr2(构造代码块赋值)
通过每执行一个代码块或构造函数,输出字段在上一代码块执行后的值,以此来探究对象的初始化顺序。
2、父类静态代码块和子类静态字段初始化的执行顺序
public class codebloactest2 { public static void main(string[] args) { child child = new child(); } } class father { public static string fatherstr = "(静态字段初始化值)"; static { system.out.println("父类静态代码块:fatherstr" + fatherstr); fatherstr = "(静态代码块赋值)"; } } class child extends father { public static string childstr = fatherstr; static { system.out.println("子类静态代码块:childstr = fatherstr" + childstr); childstr = "(静态代码块赋值1)"; } }
输出结果:
父类静态代码块:fatherstr(静态字段初始化值) 子类静态代码块:childstr = fatherstr(静态代码块赋值)
我们在子类静态字段childstr初始化的时候,赋的是父类静态字段fatherstr的值。由输出结果可知,childstr初始化后的值是父类静态代码块执行后赋予fatherstr的值。由此可知两者的执行顺序为:父类静态代码块==>子类静态字段初始化
结论:
通过结论我们可以很明显的看出:static字段、代码块的执行顺序优先于非static字段、代码块。这是因为在静态域是属于类的,在类加载后就一直存在;而普通域需要创建对象才能访问。而在创建对象时,需要先加载父类,然后再加载子类,因此父类的静态字段初始化和静态代码块执行先于子类。
引用:
方法重载(overlord)
方法重载是 java多态性的表现。在 java 语言中,在同一个类中有多个(一个以上)名称相同的方法,但它们的参数列表各不相同(即参数个数或类型不同),这种情况被称为方法的重载。
方法重载有两种情况:普通方法的重载和构造方法的重载。
重载遵循的规则:
- 重载方法的参数列表必须和被重载的方法不同,并且这种不同必须足以清楚地确定要调用哪一个方法。
- 重载方法的返回值类型可以和被重载的方法相同,也可以不同,但是只有返回值类型不同不能表示为重载。
方法的重写(override)
在java中,子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这就是方法的重写。方法重写又称方法覆盖。
在子类和父类中,重写方法后,在调用时,以创建的对象类型为准,会调用谁的方法。
重写遵循的规则:
- 发生在子父类中,方法重写的两个方法返回值、方法名、参数列表必须完全一致(子类重写父类的方法)
- 子类抛出的异常不能超过父类相应方法抛出的异常(子类异常不能大于父类异常)
- 子类方法的访问级别不能低于父类相应方法的访问级别(子类访问级别不能低于父类访问级别)
- 父类中的方法若使用private、static、final任意修饰符修饰,那么,不能被子类重写。
注意:重载(overload)与重写(override)的区别?
super关键字
可以理解为对父类的引用,使用super来调用父类的属性,方法,和构造方法
super可以完成以下的操作:
a、使用super调用父类中的属性,可以从父类实例处获得信息。
b、使用super调用父类中的方法,可以委托父类对象帮助完成某件事情。
c、使用super调用父类中的构造方法(super(实参)形式),必须在子类构造方法的第一条语句,调用父类中相应的构造方法,若不显示的写出来,默认调用父类的无参构造方法,比如:super();
final关键字
使用final关键字完成以下的操作:
a、使用final关键字声明一个常量
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的
b、使用final关键字声明的方法
该方法为最终方法,且只能被子类继承,但是不能被子类重写。
c、使用final关键字声明的类
该类就转变为最终类,没有子类的类,fianl修饰的类无法被继承。
d、在方法参数中使用final,在该方法内部不能修改参数的值。
当final变量是基本数据类型以及string类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用,不过要注意,只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化
引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。
可以参考:
抽象类(abstract)
抽象类的基本概念:
(1)很多具有相同特征和行为的对象可以抽象为一个类;很多具有相同特征和行为的类可以抽象为一个抽象类。
(2)使用abstract关键字声明的类为抽象类
定义一个抽象类:
abstract class animal{ public abstract void move(); } abstract class person extends animal{ private string name; //... public abstract void eat();//抽象方法 }
抽象类的规则:
- 抽象类可以没有抽象方法,有抽象方法的类必须是抽象类
- 非抽象类继承抽象类必须实现所有抽象方法
- 抽象类可以继承抽象类,可以不实现父类抽象方法。
- 抽象类可以有方法实现和属性
- 抽象类不能被实例化
- 抽象类不能声明为final
- 抽象类可以有构造方法
接口(interface)
接口的定义语法格式:
[可见度] interface 接口名称 [extends 其他的接口名] { // 声明变量 // 抽象方法 }
接口特性
1、接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
2、接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
3、接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
示列:
interface ieat{ //public abstract void eat(); void eat(); //默认为public abstract void eat(); //public static final int num = 10; int num = 10; } interface isleep extends ieat{ void sleep(); }
接口的使用规则:
(1)定义一个接口,使用interface关键字
(2)在一个接口中,只能定义常量、抽象方法,jdk1.8后可以定义默认的实现方法
(3)接口可以继承多个接口:extends xxx,xxx
(4)一个具体类实现接口使用implements关键字
(5)一个类可以实现多个接口
(6)抽象类实现接口可以不实现接口的方法
(7)在接口中定义的方法没有声明 访问修饰符,默认为public
(8)接口不能有构造方法
(9)接口不能被实例化
java8新增:
(1)增加了default方法和static方法,这两种方法完全可以有方法体
(2)default方法属于实例,static方法属于类(接口)
(3)接口中的静态方法不会被继承,接口中的静态变量会被继承
public interface iuser { static void say() { system.out.println("say_" + iuser.class); } default void eat() { system.out.println("eat_" + iuser.class); } }
多态性
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
多态存在的三个必要条件:
(1)继承
(2)重写
(3)父类引用指向子类对象
多态的优点
消除类型之间的耦合性、可替换性、可扩充性、灵活性、简化性等。
对象的多态性:
对象多态性是从继承关系中的多个类而来,
向上转型:将子类实例转为父类引用
格式:父类 父类对象 = 子类实例 ; 自动转换
以基本数据类型操作为例:int i = ‘a' ;
(因为char的容量比int小,所以可以自动完成)
向下转型:将父类实例转为子类实例
格式*:子类 子类对象 = (子类)父类实例 ;强制转换
以基本数据类型操作为例:char c = (char)97;
(因为整型是4个字节比char 2个字节要大,所以需要强制完成)
a、方法的重载与重写就是方法的多态性表现
b、多个子类就是父类中的多种形态
c、父类引用可以指向子类对象,自动转换
d、子类对象指向父类引用需要强制转换(注意:类型不对会报异常)
e、在实际开发中尽量使用父类引用(更利于扩展)
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,
该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,
必定是使用子类中定义的这些方法(动态连接、动态调用)。
例如:
class a{ void fun1(){} void fun2(){} } class b extends a{ void fun1(string a){} //重载fun1 void fun2(){} //重写fun2 } class c{ public static void main(string[] args){ a a = new b(); a.fun1(); //这里会调用a类的fun1方法,由于向上转型,b的fun1(string a) 会被丢弃 a.fun2(); //这里调用b的fun2方法,由于是new 的b对象,而b重写了fun2,所以会调用b的fun2 } }
对于面向对象而言,多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
java实现多态有三个必要条件(继承、重写、向上转型):
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法
基于继承实现的多态:
继承是通过重写父类的同一方法的几个不同子类来体现的
对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,
执行相同动作产生的行为也就不同。
基于接口实现的多态:
指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。
instanceof关键字
instanceof是用于检查对象是否为指定的类型,通常在把父类引用强制转换为子类引用时要使用,以避免发生类型转换异常(classcastexception)。
语法格式如下:
对象 instanceof 类型 -- 返回boolean类型值
示例:
if(homechicken instanceof chicken){ //... }
该语句一般用于判断一个对象是否为某个类的实例,是返回true,否返回false
父类的设计法则
通过instanceof关键字,我们可以很方便的检查对象的类型,但如果一个父类的子类过多,这样的判断还是显得很繁琐,那么如何去设计一个父类呢?
a、父类通常情况下都设计为抽象类或接口,其中优先考虑接口,如接口不能满足才考虑抽象类。
b、一个具体的类尽可能不去继承另一个具体类,这样的好处是无需检查对象是否为父类的对象。
内部类
内部类就是在一个类的内部定义的类。
成员内部类:
内部类对象依赖外部类对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象。
内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段。
成员内部类格式如下:
class outer { class inner{} }
编译上述代码会产生两个文件:
outer.class和outer$inner.class。
在外部创建内部类对象
内部类除了可以在外部类中产生实例化对象,也可以在外部类的外部来实例化。
那么,根据内部类生成的 *.class 文件:outer$inner.class
“$” 符号在程序运行时将替换成 “.”
所以内部类的访问:通过 “外部类.内部类” 的形式表示。
outer out = new outer(); // 产生外部类实例 outer.inner in = null; // 声明内部类对象 in = out.new inner(); // 实例化内部类对象
局部内部类:
局部内部类的特点:
局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
局部内部类只在当前方法中有效。
局部内部类中不能定义 static 成员。
局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符(public、 private 和 protected)和 static 修饰符修饰。
在局部内部类中可以访问外部类的所有成员。
在局部内部类中只可以访问当前方法中 final 类型的参数与变量。
注意:
a、局部内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
b、局部内部类对象不能使用该内部类所在方法的非final局部变量。
class outer { public void dosomething(){ class inner{ public void seeouter(){} } } }
静态内部类:
在一个类内部定义一个静态内部类:
静态的含义是该内部类可以像其他静态成员一样,没有外部类对象时,也能够访问它。静态嵌套类仅能访问外部类的静态成员和方法。
静态内部类中也无法访问外部类的非静态成员。
class outer{
static class inner{}
}
class test {
public static void main(string[] args){
outer.inner n = new outer.inner();
}
}
匿名内部类:
匿名内部类就是没有名字的内部类。
匿名类有两种实现方式:
- 继承一个类,重写其方法。
- 实现一个接口(可以是多个),实现其方法。
匿名内部类的特点:
(1) 匿名类和局部内部类一样,可以访问外部类的所有成员。如果匿名类位于一个方法中,则匿名类只能访问方法中 final 类型的局部变量和参数。
(2)匿名类中允许使用非静态代码块进行成员初始化操作。
(3)匿名类的非静态代码块会在父类的构造方法之后被执行。
在使用匿名内部类时,要记住以下几个原则:
(1)不能有构造方法,只能有一个实例。
(2)不能定义任何静态成员、静态方法。
(3)不能是public,protected,private,static。
(4)一定是在new的后面,用其隐含实现一个接口或继承一个类。
(5)匿名内部类为局部的,所以局部内部类的所有限制都对其生效
局部内部类访问局部变量必须用final修饰,为什么?
当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法被调用时会入栈,
方法结束后即弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,显然已无法使用了,
如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也就可以继续使用了。
注意:在jdk1.8中取消了在局部内部类中使用的变量必须显示的使用final修饰,编译器默认会为这个变量加上final
内部类的作用
每个内部类都能独立地继承自一个(接口的)实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,
内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
依赖外部类对象的:成员内部类,方法内部类,匿名内部类
静态内部类不依赖外部类的对象。所以,我们在项目中优先考虑选择静态内部类(不会产生内存泄露)
下一篇: javascript中的栈堆内存