龙空技术网

01 Java_面试真经_基础

端庄画板IX 93

前言:

此时朋友们对“java高手真经”大约比较着重,各位老铁们都需要分析一些“java高手真经”的相关知识。那么小编也在网上网罗了一些对于“java高手真经””的相关资讯,希望大家能喜欢,小伙伴们快快来学习一下吧!

1、面向对象的三个基本特征?

面向对象的三个基本特征是:封装、继承和多态。

代码的追求:低耦合,高内聚

(1)封装

封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改

变它内部的数据。在 Java 当中,有 3 种修饰符: public, private 和 protected。每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。

下面列出了使用封装的一些好处:

通过隐藏对象的属性来保护对象内部的状态。提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。禁止对象之间的不良交互提高模块化

(2)继承:让某个类型的对象获得另一个类型的对象的属性和方法继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

(3)封装:隐藏对象的部分属性和实现细节,对数据的访问只能通过外公开的方法。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。

(4)多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的3个条件:1)继承;2)重写;3)父类引用指向子类对象。

2、JVM、JRE和JDK的关系lJVM

Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

JRE

Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包

如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

JDK

Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等

JVM&JRE&JDK关系图

3、访问修饰符public,private,protected,以及不写时的区别?4、下面两个代码块能正常编译和执行吗?

// 代码块1

short s1 = 1; s1 = s1 + 1;

// 代码块2

short s1 = 1; s1 += 1;

代码块1编译报错,错误原因是:不兼容的类型: 从int转换到short可能会有损失”。

代码块2正常编译和执行。

我们将代码块2进行编译,字节码如下:

public class com.joonwhee.open.demo.Convert {

public com.joonwhee.open.demo.Convert();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."<init>":()V

4: return

public static void main(java.lang.String[]);

Code:

0: iconst_1 // 将int类型值1入(操作数)栈

1: istore_1 // 将栈顶int类型值保存到局部变量1中

2: iload_1 // 从局部变量1中装载int类型值入栈

3: iconst_1 // 将int类型值1入栈

4: iadd // 将栈顶两int类型数相加,结果入栈

5: i2s // 将栈顶int类型值截断成short类型值,后带符号扩展成int类型值入栈

6: istore_1 // 将栈顶int类型值保存到局部变量1中

7: return

}

可以看到字节码中包含了 i2s 指令,该指令用于将 int 转成 short。i2s 是 int to short 的缩写。

其实,s1 += 1 相当于 s1 = (short)(s1 + 1),有兴趣的可以自己编译下这两行代码的字节码,你会发现是一摸一样的。

5、基础考察,指出下题的输出结果

public static void main(String[] args) {  Integer a = 128, b = 128, c = 127, d = 127;  System.out.println(a == b);  System.out.println(c == d)}

答案是:false,true。

执行 Integer a = 128,相当于执行:Integer a = Integer.valueOf(128),基本类型自动转换为包装类的过程称为自动装箱(autoboxing)。

public static Integer valueOf(int i) {  if (i >= IntegerCache.low && i <= IntegerCache.high)  return IntegerCache.cache[i + (-IntegerCache.low)];  return new Integer(i);}

在 Integer 中引入了 IntegerCache 来缓存一定范围的值,IntegerCache 默认情况下范围为:-128~127。

本题中的 127 命中了 IntegerCache,所以 c 和 d 是相同对象,而 128 则没有命中,所以 a 和 b 是不同对象。

但是这个缓存范围时可以修改的,可能有些人不知道。可以通过JVM启动参数:-XX:AutoBoxCacheMax=<size> 来修改上限值,如下图所示:

封装类常量池

除了字符串常量池,Java的基本类型的封装类大部分也都实现了常量池。包括Byte,Short,Integer,Long,Character,Boolean

注意,浮点数据类型Float,Double是没有常量池的

封装类的常量池是在各自内部类中实现的,比如IntegerCache(Integer的内部类)。要注意的是,这些常量池是有范围的:

Byte,Short,Integer,Long : [-128~127]

Character : [0~127]

Boolean : [True, False]

测试代码如下:

public static void main(String[] args) {  Character a=129;  Character b=129;  Character c=120;  Character d=120;  System.out.println(a==b);  System.out.println(c==d);  System.out.println("...integer...");  Integer i=100;  Integer n=100;  Integer t=290;  Integer e=290;  System.out.println(i==n);  System.out.println(t==e);}

运行结果:False true...integer...true false

封装类的常量池,其实就是在各个封装类里面自己实现的缓存实例(并不是JVM虚拟机层面的实现),如在Integer中,存在IntegerCache,提前缓存了-128~127之间的数据实例。意味着这个区间内的数据,都采用同样的数据对象。这也是为什么上面的程序中,通过==判断得到的结果为true。

这种设计其实就是享元模式的应用

private static class IntegerCache {    static final int low = -128;    static final int high;    static final Integer cache[];    static {    		// high value may be configured by property   		 int h = 127;   		 String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");   		 if (integerCacheHighPropValue != null) {   				 try {   						 int i = parseInt(integerCacheHighPropValue);  						  i = Math.max(i, 127);   						 // Maximum array size is Integer.MAX_VALUE   						 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);   				 } catch( NumberFormatException nfe) {  						  // If the property cannot be parsed into an int, ignore it.   				 } 		   }   	 high = h;  	 cache = new Integer[(high - low) + 1];    int j = low;    for(int k = 0; k < cache.length; k++)   		 cache[k] = new Integer(j++);    // range [-128, 127] must be interned (JLS7 5.1.7)    assert IntegerCache.high >= 127;} private IntegerCache() {}}

封装类常量池的设计初衷其实String相同,也是针对频繁使用的数据区间进行缓存,避免频繁创建对象的内存开销。

6、Java和C++的区别

我知道很多人没学过C++,但是面试官就是没事喜欢拿咱们Java和C++比呀!没办法!!!就算没学过C++,也要记下来!

都是面向对象的语言,都支持封装、继承和多态Java不提供指针来直接访问内存,程序内存更加安全Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。Java有自动内存管理机制,不需要程序员手动释放无用内存7、面向对象五大基本原则是什么?

(1)单一职责原则SRP(Single Responsibility Principle)

类的功能要单一,不能包罗万象,跟杂货铺似的。

(2)开放封闭原则OCP(Open-Close Principle)

一个模块对于拓展是开放的,对于修改是封闭的。

(3)里式替换原则LSP(the Liskov Substitution Principle LSP)

子类可以替换父类出现在父类能够出现的任何地方。

(4)依赖倒置原则DIP(the Dependency Inversion Principle DIP)

高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

(5)接口分离原则ISP(the Interface Segregation Principle ISP)

设计时采用多个与特定客户类有关的接口比采用一个通用的接口要好。

8、接口和抽象类的区别9、&和&&的区别?

&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。

&:逻辑与运算符、按位与运算符

按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。

逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。

10、String 类可以继承吗?

不行。String 类使用 final 修饰,无法被继承。

11、String和StringBuilder、StringBuffer的区别?

String:String 的值被创建后不能修改任何对 String 的修改都会引发新的 String 对象的生成

StringBuffer:跟 String 类似,但是值可以被修改,使用 synchronized 来保证线程安全

StringBuilder:StringBuffer 的非线程安全版本,没有使用 synchronized,具有更高的性能,推荐优先使用。

12、String s = new String("xyz") 创建了几个字符串对象?

一个或两个。如果字符串常量池已经有“xyz”,则是一个;否则,两个

当字符创常量池没有 “xyz”,此时会创建如下两个对象:

一个是字符串字面量 "xyz" 所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。

另一个是通过 new String() 创建并初始化的,内容与"xyz"相同的实例,也是在堆中。

在JDK1.8之前字符串常量池是在方法区的运行时常量池中的,而方法区属于永久代中的;而JDK1.8之后字符串常量池被放到了堆中。因此在JDK1.8版本之后字符串常量池中存放的是字符串对象以及字符常量值。

13、String s = "xyz" 和String s = new String("xyz") 区别?

两个语句都会先去字符串常量池中检查是否已经存在 “xyz”,如果有则直接使用,如果没有则会在常量池中创建 “xyz” 对象。

另外,String s = new String("xyz") 还会通过 new String() 在堆里创建一个内容与 "xyz" 相同的对象实例。

所以前者其实理解为被后者的所包含。

示例一:String s1 = "a";String s2 = "b";String s3 = "a" + "b";String s4 = s1 + s2;String s5 = "ab";String s6 = s4.intern(); System.out.println(s3 == s4);//falseSystem.out.println(s3 == s5);//trueSystem.out.println(s3 == s6);//true String x2 = new String("c")+new String("d");String x1 = "cd";x2.intern(); //如果调换了倒数第二行和倒数第三行的位置呢?如果换成JDK1.6呢?System.out.println(x1 == x2);//false;如果调换位置true;如果换成jdk1.6是false实例二:public static void main(String[] args) {        String s = new String("a") + new String("b");         String s2 = s.intern();        System.out.println(s2 == "ab");        System.out.println(s == "ab");    }

结果:

说明:程序开始执行到new String("a")时,判断字符串常量池中是否有字符常量"a",发现没有则首先将字符串常量"a"放入到字符串常量池中,然后在堆中创建一个值为"a"的对象temp1;同理将字符串常量"b"放入到字符串常量池中,接着在堆中创建一个值为"b"的字符串对象temp2,最后使用new String(new StringBuilder().append(temp1).append(temp2).toString())在堆中创建一个值为"ab"的字符串对象,并将这个对象的引用赋值给s。

在执行s.intern()方法时,尝试将s指向的字符串对象放入到字符串常量池中,并返回串池中的对象,如果放入成功,无论s2还是s都是指向串池中的"ab"字符串,自然返回true。【需要注意这是jdk1.7及以后才会出现的,同时因为s.intern()方法是将s指向的对象放入串池,因此刚才在堆中的s指向的字符串对象现在是在串池中了。如果是jdk1.6运行结果是:true,false,这是因为在执行s.intern方法的时候会拷贝一份s对象放入到串池中,并返回这个拷贝对象的引用给s2,而s还是指向堆中的原对象】

示例三:public static void main(String[] args) {        String x = "ab";        String s = new String("a") + new String("b");        String s2 = s.intern();        System.out.println(s2 == x);        System.out.println(s == x);    }

运行结果:

说明:程序开始执行到x = "ab"的时候检查串池发现串池中没有字符串常量“ab”,因此把"ab"放入到串池中,运行到new String("a")的时候会把常量"a"放入到串池中,并且在堆中创建一个值为"a"的字符串对象temp1,同理会把常量"b"放入到串池中并且在堆中创建一个值为"b"的字符串对象temp2,最后使用new String(new StringBuilder().append(temp1).append(temp2).toString())在堆中创建一个值为"ab"的字符串对象,并将对象引用赋值给s。执行到s.intern()方法时检查字符串常量池,发现"ab"已经在字符串常量池中,则s引用还是指向堆中值为"ab"的字符串对象不会改变,同时将串池中的"ab"引用赋值给s2,因此s2是指向串池中的"ab"的,而s不是指向串池中的"ab",最后结果就是true和false。【如果是jdk1.6运行结果是:true,false】

14、用最有效率的方法计算2 乘以8

2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)

15、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少

Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5 然后进行下取整。

16、float f=3.4;是否正确

不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F;。

17、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗

对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。

而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。

标签: #java高手真经