1、static关键字
1.1、static关键字可以修饰什么?static使用的注意事项有哪些?static关键字的特点?使用static存在什么问题?
可以用来修饰:成员变量,成员方法,代码块,内部类等。具体如下所示
- 修饰成员变量和成员方法
- 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。
- 被static 声明的成员变量属于静态成员变量,静态变量存放在Java内存区域的方法区。
- 静态代码块
- 静态代码块定义在类中方法外,静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。
- 该类不管创建多少对象,静态代码块只执行一次。
- 静态内部类
- static修饰类的话只能修饰内部类,static应用于外部类没有意义,或者说外部类本就是隐式静态的。
- 静态内部类与非静态内部类之间存在一个最大的区别:
- 非静态内部类在编译完成之后会保存一个创建它的外部类的引用,但是静态内部类却没有。
- 没有这个引用就意味着:
- 静态内部类的创建不需要依赖外部类的创建。
- 静态内部类不能使用任何外部类的非static成员变量和方法。
- 非静态内部类在编译完成之后会保存一个创建它的外部类的引用,但是静态内部类却没有。
- 静态导包(用来导入类中的静态资源,1.5之后的新特性):
- 这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
static使用的注意事项有哪些?
- 在静态方法中是没有this关键字的
- 静态是随着类的加载而加载,this是随着对象的创建而存在。
- 静态比对象先存在。
- 静态方法只能访问静态的成员变量和静态的成员方法(静态只能访问静态,非静态可以访问静态的也可以访问非静态的)
static关键字的特点?
- 随着类的加载而加载
- 优先于对象存在
- 被类的所有对象共享
- 可以通过类名调用(静态修饰的内容一般我们称其为:与类相关的,类成员)
使用static存在什么问题?
- 占用内存,并且内存一般不会释放;
- 在系统不够内存情况下会自动回收静态内存,这样就会引起访问全局静态错误。
- 在Android中不能将activity作为static静态对象,这样使activity的所有组件对象都存入全局内存中,并且不会被回收;
1.2、static变量存储位置是哪里?静态变量的生命周期?静态变量何时销毁?静态引用的对象回收如何理解?
static变量存储位置
- static变量在类加载时被初始化,存储在JVM的方法区中,整个内存中只有一个static变量的拷贝。
- 静态变量的生命周期
- 当启动app时,系统会创建一个进程,此进程会加载一个Dalvik VM的实例,然后代码就运行在DVM之上,类的加载和卸载,垃圾回收等事情都由DVM负责。也就是说在进程启动的时候,类被加载,静态变量被分配内存。
- 静态变量何时销毁
- 类在什么时候被卸载?在进程结束的时候。
- 一般情况下,所有的类都是默认的ClassLoader加载的,只要ClassLoader存在,类就不会被卸载,而默认的ClassLoader生命周期是与进程一致的。
- 静态引用的对象回收
- 不会被垃圾回收。单例对象在运行时不会被回收。
2、final,finally,finalize有什么不同?finally什么情况下不会被执行?java.lang 包下为什么要设置final?
2.1、final可以修饰类,方法,变量
- final修饰类代表类不可以继承拓展。
- final修饰变量表示变量不可以修改。
- final修饰方法表示方法不可以被重写。
2.2、finally则是Java保证重点代码一定要被执行的一种机制
- 可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC连接、保证 unlock 锁等动作。
2.3、finalize 是基础类 java.lang.Object的一个方法
- 它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9开始被标记为 deprecated。
2.4、在以下4种特殊情况下,finally块不会被执行
- 在finally语句块中发生了异常。
- 在前面的代码中用了System.exit()退出程序。
- 程序所在的线程死亡。
- 关闭CPU。
2.5、java.lang 包下为什么要设置final?
- final 变量产生了某种程度的不可变(immutable)的效果,所以,可以用于保护只读数据,尤其是在并发编程中,因为明确地不能再赋值 final 变量,有利于减少额外的同步开销,也可以省去一些防御性拷贝的必要。
- 使用 final 修饰参数或者变量,也可以清楚地避免意外赋值导致的编程错误,甚至,有人明确推荐将所有方法参数、本地变量、成员变量声明成 final。
3、原始数据类型和引用类型局限性?为何要引用基本数据包装类?基本数据类型一定存储在栈中吗?
3.1、原始数据类型和引用类型局限性
- 原始数据类型和 Java 泛型并不能配合使用
- Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧,Java 编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为Object。
3.2、为何要引用基本数据包装类
- 使用泛型,需要用到基本数据类型的包装类。
- Java 的对象都是引用类型,如果是一个原始数据类型数组,它在内存里是一段连续的内存,而对象数组则不然,数据存储的是引用,对象往往是分散地存储在堆的不同位置。这种设计虽然带来了极大灵活性,但是也导致了数据操作的低效,尤其是无法充分利用现代 CPU 缓存机制。
- Java 为对象内建了各种多态、线程安全等方面的支持,但这不是所有场合的需求,尤其是数据处理重要性日益提高,更加高密度的值类型是非常现实的需求。
3.3、基本数据类型一定存储在栈中吗?
- 基本数据类型是放在栈中还是放在堆中,这取决于基本类型在何处声明,下面对数据类型在内存中的存储问题来解释一下:
- 在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因:
- 在方法中声明的变量可以是基本类型的变量,也可以是引用类型的变量。当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在JAVA虚拟机栈中。当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在JAVA虚拟机的栈中,该变量所指向的对象是放在堆类存中的。
- 在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因:
- 在类中声明的变量是成员变量,也叫全局变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)。同样在类中声明的变量即可是基本类型的变量,也可是引用类型的变量:
- 当声明的是基本类型的变量其变量名及其值放在堆内存中的。引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中。