传统C/C++等编程语言需要程序员显示进行垃圾回收,显示进行垃圾回收主要有以下两个缺点:
1. 程序忘记回收无用内存,从而导致内存泄漏,降低系统性能。
2. 程序错误地回收程序核心类库的内存,从而导致系统崩溃。
JAVA程序的内存分配和回收都是由JRE在后台自动进行的。JRE会负责回收那些不在使用的内存,这种机制被称为垃圾回收(Garbage Collection,简称GC)
通常JRE会提供一条超级线程来进行检测和控制,一般都是在CPU空闲
或内存不足时
自动进行垃圾回收,而程序员无法精准控制垃圾回收的时间和顺序。
Java语言规范没有明确的说明JVM使用的是哪种垃圾回收算法,但任何垃圾回收算法一般要做两件事: 1.发现无用对象
;2.回收被无用对象占据的内存空间,使该空间可以被程序再次使用
;
垃圾回收特点:
回收无用对象的内存空间
,这些内存空间都是JVM堆内存
里的内存空间,垃圾回收只能回收内存资源
,对其他物理资源,如数据库连接、磁盘IO等资源则无能为力;该对象的引用置为null
,通过这种方式暗示垃圾回收机制可以回收该对象。不可预知性
:由于不同JVM才用了不同的垃圾回收机制和不同的垃圾回收算法,因此它有可能是定时发生
,有可能适当CPU空闲
时发生,也有可能是和原始垃圾回收一样,等到内存消耗出现极限
时发生,这些和垃圾回收机制的选择和具体的设置都有关系。虽然程序员可以通过调用该对象的finalize()
方法或system.gc()
等方法来建议系统进行垃圾回收,但这种调用仅仅是建议,依然不能精确
控制垃圾回收机制的执行。标记
活着的对象;二、垃圾回收器能够精确地定位对象之间的引用关系
。前者是完全回收所有废弃对象的前提,否则就可能造成内存泄漏。而后者是实现归并
和复制
等算法的必要条件,通过这种引用关系,可以保证所有对象都能被可靠地回收,所有对象都能被重新分配,从而有效的减少内存碎片
的产生。停止应用程序的运行
,有的当垃圾回收运行时,同时允许应用程序的线程
运行,还有的在同一时间垃圾回收多线程
运行。特点(简化版):
堆内存
中的对象,不会回收任何物理资源。finalize
方法,该方法可能使对象重新复活,从而导致垃圾回收机制取消回收。在我们编写Java程序时,一个基本原则是:对于不再需要的对象,不要引用它们。
如果保持了对这些对象的引用,垃圾回收机制暂时不会回收该对象,则会造成系统可用内存越来越少,这时垃圾回收执行的频率也越高,从而导致系统的性能下降。
System.gc();
Runtime.getRuntime().gc();
这种强制只是通知系统进行垃圾回收,系统是否会进行垃圾回收不确定。
finalize方法
该方法定义在Object类中,原型为:protected void finalize() throws Throwable
任何Java类都可以覆盖finalize方法,在该方法中清理资源。
finalize特点:
不确定性
。不会报异常
,程序继续执行。Java语言对象的引用有四种:
强应用
:程序通过引用变量来操作实际对象,被强引用的对象不能被垃圾回收机制回收。软引用(SoftReference)
:通过SoftReference
类实现。如果一个对象只有软引用,当系统空间足够时,不会被系统回收,程序也可以使用该对象;当系统空间不足时,系统将会回收它。软引用通常用于对内存敏感的程序中。弱引用(WeakReference)
:通过WeakReference
类实现。弱引用比软引用的引用级别更低。如果一个对象只有弱引用,当垃圾回收运行时,那么无论系统内存是否足够,该对象都会被回收。虚引用(PhantomReference)
:虚引用完全类似没有引用。虚引用主要用于跟踪对象被垃圾回收的状态。虚引用不能单独使用,必须和引用队列(ReferenceQueue)联合使用。上面三个引用类都包含了一个get()方法,用于获取被他们所引用的对象。
举一个弱引用的例子:
public static void main(String[] strs) {
String str = new String("哈哈哈");
// String str = "哈哈哈";
//创建引用队列
ReferenceQueue rq = new ReferenceQueue();
//创建一个虚引用
PhantomReference pr = new PhantomReference(str, rq);
//创建弱引用对象
// WeakReference wr = new WeakReference(str);
str = null;
System.out.println("去除虚引用所引用的对象:" + pr.get());
// System.out.println("弱引用被清理前:" + wr.get());
System.gc();
System.runFinalization();
// System.out.println("弱引用被清理后:" + wr.get());
System.out.println("取出引用队列中最先进入队列的引用与pr比较:" + (rq.poll() == pr));
}
被注释的部分是一个弱引用对象的简单举例,未被注释的部分是虚引用 + 引用队列的举例。
好处:
注意:要使用这些特殊的引用类,就不能保存对对象的强引用。
当程序希望从弱引用中取出引用对象时,该对象可能已经被回收了,这时就要重新创建对象。
obj = wr.get();
if(obj == null){
//重新创建一个对象,再次使用虚引用引用它
wr = new WeakReference(recreateIt());//1
//取出虚引用
obj = wr.get();2
}
...操作obj对象...
//再次切断obj对象强引用
obj = null;
这种写法有一定问题,由于垃圾回收的不确定性,如果系统在1的时候又将弱引用对象回收了,那么在程序运行到2时,所获取的对象还是null。正确写法应该是:
obj = wr.get();
if(obj == null){
obj = recreatIt();
wr = new WeakReference(obj );
}
...操作obj对象...//这时用强引用直接操作obj对象
//再次切断obj对象强引用
obj = null;
答:不被程序引用的对象。
引用计数法(存在缺陷)
可达性分析算法:虚拟机会将一些对象定义为GC Roots,从GC Roots出发一直沿着引用链向下寻找,如果某个对象不能通过GC Roots寻找到,那么虚拟机就认为该对象可以被回收。
满足以下条件的对象可以看做是GC roots:
栈
中引用的对象。静态属性
引用的对象。常量
引用的对象。本地方法栈
中JNI(Native方法)的引用的对象。答:由于JVM的垃圾回收机制和不同垃圾回收机制算法不同,可能是在CPU空闲时、可能定时发生、也可能是内存消耗极限时。
…
API文档主要用于说明类、方法、属性的功能,因此javadoc工具只处理文档源文件在类、接口、方法、属性、构造器和内部类之前的注释,忽略其他地方的文档注释。并且javadoc只处理public
和protected
修饰的类、方法和属性。如果希望提取privated修饰的内容,则可以在使用javadoc工具室添加-private
选项。
javadoc常用选项:
-d
-windowtitle
窗口标题
。doctitle
-header
举例:
javadoc -d apidoc -windowtitle 测试 -doctitle 学习javadoc工具的测试API文档 -header 自定义类 Test*.java
abstract | assert | boolean | break | byte | case |
---|---|---|---|---|---|
catch | char | class | continue | default | do |
double | else | enum | extends | final | finally |
float | for | if | implements | import | int |
interface | instanceof | long | native | new | package |
private | protected | public | return | short | static |
strictfp | super | switch | synchronized | this | throw |
throws | transient | try | void | volatile | while |
除了上面48个关键字,java还包含两个保留字:goto
和const
Java还提供了3个特殊的直接量(literal):true
、false
和null
Java支持的类型氛围两类:基本类型
和引用类型
。
基本类型包括boolean类型
和数值类型
。
数值类型有整数类型
和浮点类型
。
整数型包括byte、short、int、long、char,浮点类型包括float、和double。
注意:有的时候也把char型称为字符型,实际上字符型也是一种整数型。
注意:空引用(null)只能被转换成引用类型,不能被转换成基本类型。
byte
:一个byte型整数在内存里占8位,表数范围是-128~127(2的7次方)。short
: 一个short型整数在内存里占16位,表数范围是-32768~32767(2的15次方).int
: 一个int型整数在内存里占32位,表数范围是-2147483648到2147483647(2的31次方)long
: 一个long型整数在内存里占64位,表数范围是-9223372036854775808~9223372036854775807。(2的63次方)Java中整数常量有3种表示方式:10进制、8进制和16进制。
8进制整数常量以0开头。16进制的整数以0x或0X开头。
Java语言使用Unicode编码集
作为编码方式,Unicode支持世界上所有书面语言字符,因此Java支持各种语言字符。
字符常量有三种表现方式:
单个字符
来指定字符常量,例如,‘A’,‘9’,‘0’等。转义字符
表示特殊字符常量,例如,‘\n’,‘\t’等。Unicode值
来表示字符常量,格式是’\uXXXX’,其中XXXX表示一个16进制的整数。转义字符 | 说明 | Unicode表示方式 |
---|---|---|
\b | 退格符 | \u0008 |
\n | 换行符 | \u000a |
\r | 回车符 | \u000d |
\t | 制表符 | \u0009 |
" | 双引号 | \u0022 |
’ | 单引号 | \u0027 |
\ | 反斜杠 | \u005c |
char类型的值也可以直接作为整数型
的数值来使用,但它是一个16位无符号整数,即全部是正数,表数范围是0~65535。
Java浮点类型有两种:float
和double
。
float
: 单精度浮点数,占4个字节,32位。第一位是符号位,接下来8位表示指数,接下来23位表示尾数。double
: 双精度浮点数,占8个字节,64位。第一位是符号位,接下来11位表指数,接下来52位表尾数。Java浮点数有两种表现形式:
注意:Java浮点数使用二进制的科学计数法来表示,因此可能不精确标识一个浮点数。虽然double比float更精准,但是还是可能出现这种情况。如果要精确保存一个浮点数,可以考虑使用
BigDecimal
类。精确浮点运算传送门
只有浮点类型
的数值才能用科学计数法表示。例如51200是一个int类型的值,512E2则是一个浮点型的值。
Java还提供了三个特殊的浮点数值:正无穷大
、负无穷大
和非数
,用于表示溢出和出错。
正无穷大
:正数除以0得到正无穷大。可以通过Double或Float的POSITIVE表示。负无穷大
:负数除以0的到负无穷大。可以通过Double或Float的NEGATIVE_INFINITY表示。非数
:0.0除以0.0或对负数开方得到一个非数。非数通过Double或Float的NaN表示。所有正无穷大数值都是相等的,所有负无穷大数值也是相等的,而NaN不与任何数值相等,甚至不和NaN相等。
注意:只有
浮点数
除以0才可以得到正无穷大或负无穷大。因为Java会自动把和浮点数运算的0当成0.0处理。如果一个整数除以0,则会抛异常。
答:int占4个字节,char占2个字节,long占8个字节。
答:-11和12
1、小数点后第一位=5
2、正数:Math.round(11.5)=12
3、负数:Math.round(-11.5)=-11
4、
5、小数点后第一位<5
6、正数:Math.round(11.46)=11
7、负数:Math.round(-11.46)=-11
8、
9、小数点后第一位>5
10、正数:Math.round(11.68)=12
11、负数:Math.round(-11.68)=-12
总结规律:
- 小数点后第一位小于5:直接舍去小数部分。
- 小数点后第一位等于5:正数直接加1,负数舍去小数部分。
- 小数点后第一位大于5:整数部分绝对值加1,正负不变。
Java支持的运算符有:
&
: 按位与。|
:按位或。~
: 按位非。^
: 按位异或。<<
:左位移运算符。>>
:右位移运算符。>>>
:无符号右移运算符。第一个运算数 | 第二个运算数 | 按位与 | 按位或 | 按位异或 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 1 |
1 | 0 | 0 | 1 | 1 |
0 | 1 | 0 | 1 | 0 |
~
运算符运算规则:
先说一下原码、反码与补码。
1.原码
:符号位加上数字的二进制表示,int为例,第一位表示符号
(0正数 1负数),对于原码来说,绝对值相等的正数和负数只有符号位不同。
2.反码
:如果一个数为正,则与补码相同。如果一个数为负,则符号位为1(符号位不变化,其余位数取反
)
3.补码
:如果一个数如果为正,则它的原码、反码、补码相同;一个数如果为负,去到反码然后加1(反码加1就是补码
)。Java使用补码表示负数。
负数转二进制步骤:-3
1. 首先将负数转换为对应的原码。
0000 0000 0000 0000 0000 0000 0000 0011
2. 再将原码按位取反,得到反码。
1111 1111 1111 1111 1111 1111 1111 1100
3. 将反码+1得到补码。
1111 1111 1111 1111 1111 1111 1111 1101
这就是负数的二进制表示了。
逆转这个过程得到十进制的数。1111 1111 1111 1111 1111 1111 1111 1101
1. 先取反,得到反码:
0000 0000 0000 0000 0000 0000 0000 0010
2. 反码+1,得到:
0000 0000 0000 0000 0000 0000 0000 0011
3. 转为10进制,最后的到3。
注意:如果int数值类型位数不满32位,需要在高位补0。
Java的右移运算符有两个,>>和>>>
1. >>
:把操作时的二进制码右移指定位数后,左边空出来的位数以符号位
补充。
2. >>>
:把操作时的二进制码右移指定位数后,左边空出来的位数以0
补充。
>>
、>>>
和<<
三个位移运算只适合对byte、short、char、int和long等整型进行运算,同时还有以下运算规则:
a>>b
,系统先用32对b取余后再进行运算,例如:a>>33和a>>1结果相同,a>>32和a结果一样。数值型对应的包装类的实例是可以与数值型的值进行比较的,这种比较是直接取出包装类所包装的数值
进行比较,也就是自动拆箱
比较。
在看下面这段代码:
同样的两个Integer实例,装箱后结果为什么不一样呢?
这跟Integer的设计有关:
从上面的代码可以看出,系统把一个-128~127
之间的数字自动装箱成Integer,并放入了一个名为cache的数组保存起来。当有这个范围的数值需要装箱时,直接引用到cache数组中对应的值,所以在这个范围内的数组装箱后全部相等
。
与此类似的还有String类,String类也会对通过直接量直接赋值的String实例进行缓存。如图:
使用break
结束一个循环:break用于完全结束一个循环,跳出循环体。
break
不仅可以结束其所在的循环,还可以结束其外层循环:
break后紧跟一个标签语句,会让程序直接结束outer指定的循环。
注意:break后必须是一个有效标签,且必须再break所在循环的外层循环之前定义才有意义。
continue·
可以用于终止本次循环。
与break类似,continue
也可以紧跟一个标签,用于直接结束标签所标识循环的当次循环,重新开始下一次循环。
用法与注意与break一致。
定义数组语法:
type[]是一种新类型,与type类型完全不同。例如int是基本类型,int[]是引用类型。
注意:定义数组时不能指定数组长度(动态初始化时可以指定长度)。
注意:foreach循环迭代数组元素时,并不能改变数组元素的值,因此不要对foreach的循环变量进行赋值。
实际数组元素
被存储在堆
(heap)内存中;数组引用变量是一个引用类型变量
,被存储在栈
(stack)内存中。
static修饰的成员不能访问没有static修饰的成员。
静态成员不能直接访问非静态成员。
static修饰的方法无论是类本身
或是对象
调用都会得到相同的执行结果。
基本类型参数传递的是值,即值传递
。就是将实际参数值得副本(复制品)传入方法内,而参数本身不会受到任何影响。
引用类型参数传递的是地址,即址传递
。
其实参数传递的也是值,从底层来看,引用对象是将引用对象拷贝了一份传到方法中,此时堆内存存放的真正对象有两个引用变量,用图说明:
这两个引用变量确实是两个,但是引用的对象本身只有堆内存的唯一一个。所以现在无论哪个引用变量为对象赋值,改变的都是同一个对象。
更直观的证明一下,可以在调用完swap方法后,将dw对象引用指向null,就变为:
此时swap中的引用断了,但main方法栈中引用不受影响,这确实是两个引用对象。当main方法栈中的引用也断了时,堆内存中的对象将没有引用,就会变为一个垃圾对象,垃圾回收机制将在适当时机来回收。
JDK1.5之后,Java允许定义形参长度可变的方法,定义方式与规则如下:
//长度可变形参
public static void sayList(String... strs) {
System.out.println("传入参数是:");
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
}
//调用:
sayList("李森","大锤","大宝");//使用多个参数 调用长度可变形参
sayList(new String[]{"李森","大锤","大宝"});//使用数组 调用长度可变形参
一个类在使用之前要经过类加载、类验证、类准备、类解析类初始化等几个阶段。
以Person类为例,系统会在第一次使用Person类时加载这个类,并初始化这个类。
在类的准备阶段,系统将会为该类的类属性
分配内存空间,并指定默认初始值。如图:
在系统创建一个Person对象时,实例属性是在创建实例时分配内存空间、并指定初始值的。
局部变量定义后,必须经过显示初始化
后才能使用,系统不会为局部变量执行初始化。直到程序为这个变量赋值时,系统才会为局部变量分配内存。
与成员变量不同,局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存
中。
基本类型
变量,则直接把这个变量的值保存在该变量对应的内存中;引用类型
变量,则这个变量里存放的是地址,通过该地址引用到该变量实际引用的对象或数组。private
:类访问权限。只能在该类的内部访问。default
:包访问权限。可以被相同包下其他类访问。protected
:子类访问权限。即可以被同一包其他类访问,也可以被不同包下子类访问。public
:公共访问权限。可以被所有类访问。private | default | protected | public | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中 | √ | √ | √ | |
子类中 | √ | √ | ||
全局范围内 | √ |
注意:如果一个Java源文件里的所有类都没有使用public修饰,则这个Java源文件的文件名可以是一切合法文件名;但如果一个Java源文件里定义了一个public修饰的类,则这个源文件的文件名必须与public类的类名相同。
package语句必须作为源文件第一句非注释语句。
一个源文件只能有一个包(只包含一条package语句)。
Java包机制需要两个方面的保证:
1. 源文件里使用package语句指定包名。
2. class文件必须放在对应的路径下。
import
语句:*
可以表示包下所有类,而不能表示包。
例如:import package.subpackage.*;表示引入subpackage下所有类。
import static
语句:是JDK1.5引入的一种静态导入的语法,用于导入指定类的某个静态属性或全部静态属性值。*
表示类下全部属性名。
例如:import staticpackage.subpackage.ClassName.*;
java.lang
:包含Java语言核心类,如String、Math、System和Thread等,系统会自动导入这个包下所有类。java.util
:包含Java大量工具类/接口和集合框架/接口,如Arrays、List和Set等。java.net
:包含Java网络编程相关类/接口。java.io
:包含Java输入/输出编程相关的类/接口。java.text
:包含一些Java格式化相关的类。java.sql
:包含Java进行DJBC数据库编程的相关类/接口。java.awt
:包含抽象窗口工具集(Abstract Window ToolKits)的相关类/接口,这些类主要用于构建图形用户界面(GUI)程序。java.swing
:包含Swing图形用户界面编程的相关类/接口,这些类可用于构建平台无关的GUI程序。方法的重写要遵循“两同两小一大”,
“两同”
:方法名相同,形参列表相同。“两小”
:子类方法返回值应比父类方法返回值类型更小或相等。子类方法声明抛出的异常应比父类的方发声明抛出的异常类更小或相等。“一大”
:子类方法的访问权限应比父类方法更大或相等。注意:覆盖方法和被覆盖的方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
答:可以,子类可以重写父类静态方法。
直接用一段代码来说明:
class Animal {
private void beat() {
System.out.println("心脏跳动。。。");
}
public void breach() {
beat();
System.out.println("吸一口气,吐一口气,呼吸中。。。");
}
}
class Wolf {
//将要组合部分嵌入类中属性,实现组合(有点类似于代理模式)
private Animal animal;
public Wolf(Animal animal) {
this.animal = animal;
}
public void run() {
//直接复用Animal提供的breath方法
animal.breach();
System.out.println("在陆地快速奔跑。。。");
}
}
public class ZuHeDemo {
public static void main(String[] strs) {
Animal animal = new Animal();
Wolf wolf = new Wolf(animal);
wolf.run();
}
}
接口与抽象类特征:
接口与抽象类用法差别:
答:接口与抽象类的差别非常大,这种差别主要体现在二者的设计目的上。总的来说接口是对动作的抽象,抽象类是对根源的抽象。
接口体现的是一种规范。在程序中使用接口时,接口是多个模块间的耦合标准。接口是一个契约,定义接口一定要慎重,因为一旦接口修改,对整个系统甚至其他系统的影响将是辐射式的,导致系统中大部分类都需要改写。
抽象类体现的是一种模板式的设计。
成员内部类是一种与属性、方法构造器和初始化相似的类成员,局部内部类和匿名内部类则不是类成员。
非静态内部类中不能有静态方法、静态属性、静态初始化块。
非静态内部类默认持有外部类的引用,这也是容易造成内存泄漏的一个危险点。
注意:静态内部类里不可以有静态初始化代码块,但可以包含普通初始化块。
答:把一个类放到另一个类的内部定义,这个定义在其他类内部的类就被称为内部类。
内部类作用:
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内。
- 内部类成员可以直接访问外部类的私有数据,因为内部类被当做其外部类成员,同一个类的成员可以相互访问。但外部类不可以访问内部类细节。
- 匿名内部类适合用于创建那些仅需要一次使用的类。
静态内部类可以包含静态成员,可以包含非静态成员。
静态内部类也可以定义在接口里。
匿名内部类应该遵循两条规则:
代码是最好的实践,直接通过代码说明:
//程序员
class Programmer{
private String name;
public Programmer(String name) {
this.name = name;
}
public void work(){
System.out.println(name + "正在敲代码...");
}
}
//教师
interface Teacher{
void work();
}
//既是程序员也是教师
class TeachProgrammer extends Programmer{
public TeachProgrammer(String name) {
super(name);
}
public void teach(){
System.out.println("教师正在台上讲课...");
}
private class Closure implements Teacher{
@Override
public void work() {
teach();
}
}
public Teacher getCallBack(){
return new Closure();
}
}
public class ClosureCallBackDemo {
public static void main(String[] strs) {
TeachProgrammer tp = new TeachProgrammer("白泽");
//直接调用从程序员继承的工作方法(work)
tp.work();
//表面上调用Teacher的work(),实际上回调的是teach()方法
tp.getCallBack().work();
}
}
内部类可以很方便的调用其外部类的属性方法,这样可以让编程更加灵活。
枚举类与普通类区别:
java.lang.Enum
类,而不是Object
。其中Enum实现了java.lang.Serializable
和java.lang.Comparable
两个接口。private
修饰。显示列出
。列出这些实例时,系统会默认添加public static final
修饰符。values
方法,可以遍历所有枚举值。举一个支付状态的枚举类:
interface PayStateInterface {
void info();
}
public enum PayState implements PayStateInterface {//支付状态(0未支付 10 已支付)
WAIT_PAY(0) {
public void info() {
System.out.println("这个状态是待支付状态");
}
}, ALREAD_PAY(10) {
public void info() {
System.out.println("这个状态是已支付");
}
};
private final int value;
private PayState(int value) {
this.value = value;
}
public static PayState valueOf(int type) {
switch (type) {
case 0:
return WAIT_PAY;
case 10:
return ALREAD_PAY;
default:
return null;
}
}
public int getValue() {
return value;
}
}
举例代码中还用到了枚举类实现接口知识点,可以根据不同实例来实现各自方法,也可以由枚举类实现一个公共的方法。
java.lang.Enum类中提供了一些方法:
int compareTo(E o)
:用于与指定枚举对象比较顺序,如果该枚举对象位于指定枚举对象之后,返回正整数,位于指定枚举对象之前,返回负整数,否则返回0。int ordinal()
;返回枚举值在枚举类中的索引值。(第一个枚举值的索引值为0)public static > T valueOf(Class enumType,String name)
:用于返回指定枚举类中指定名称的枚举值。String toString()
:返回枚举常量的名称。String name()
:与toString()类似。顶层类/接口 | 成员属性 | 方法 | 构造器 | 初始化块 | 成员内部类 | 局部成员 | |
---|---|---|---|---|---|---|---|
public | √ | √ | √ | √ | √ | ||
protected | √ | √ | √ | √ | |||
包访问控制符 | √ | √ | √ | √ | O | √ | O |
private | √ | √ | √ | √ | |||
abstract | √ | √ | √ | ||||
final | √ | √ | √ | √ | √ | ||
static | √ | √ | √ | ||||
strictfp | √ | √ | √ | ||||
synchronized | √ | ||||||
native | √ | ||||||
transient | √ | ||||||
volatile | √ |
strictfp
关键字含义是FP-strict,就是精确浮点
的意思。如果想让浮点运算更加精准,就可以使用strictfp修饰类、接口和方法。修饰的范围内Java编译器和运行环境会完全依照浮点规范IEEE-754来执行。native
:使用native修饰的方法类似于一个抽象方法。如果某个方法需要利用平台相关特性
,或者访问系统硬件时,则可以把该方法使用native修饰,再把该方法交给C
去实现。一旦Java程序中包含native方法,这个程序将失去跨平台的功能。答:简单地说:JRE包含JVM。
JVM是运行Java程序的核心虚拟机,而运行Java程序不仅需要核心虚拟机,还需要其他的类加载器,字节码校验器以及大量的基础类库。JRE出了包含JVM之外,还包含运行Java程序的其他环境支持。
注意:所有Java关键字都是小写的,true、false和null都不是Java关键字。
答:当一个方法执行时,每个
方法
都会建立自己的内存栈
,在这个方法内定义的变量
会逐个放在这块内存栈里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区
中,以便反复利用(因为对象创建的成本通常较大),这个运行时数据区就是堆内存
。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会销毁。只有一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在合适的时候回收它。
答:对于int f()和String f()两个方法,如果这样调用:int result = f();,系统可以识别我们是想用返回值类型为int的方法;但java调用方法时可以忽略返回值,如果这样调用:f();,可以判断调用哪个方法吗?Java系统也会糊涂。系统一糊涂,肯定就是你错了。因此,java里不能使用返回值类型作为区分方法重载的依据。
答:非静态内部类对象必须寄存在外部类对象里,而外部类对象则非必须有非静态内部类对象寄存在其中。因此外部类访问访问费静态内部类成员时,可能非静态内部类对象根本不存在,为非静态内部类访问外部类成员时,外部类对象一定存在。