面试之Java语言基础
1、标识符的命名规则
标识符只能由数字、字母(a-z、A-Z)、下划线( _ )和$组成,并且第一个字符不能为数字。
2、instanceof关键字的作用
用法:对象 A instanceof 类B。
instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。注意:如果对象A为null,则返回false。
3、strictfp关键字的作用
strictfp可以用来修饰一个类、接口或者方法,在所声明的范围内,所有浮点数的计算都是精确的。当一个类被strictfp修饰时,所有方法默认也被strictfp修饰。
4、什么是不可变类?
不可变类:当创建了一个类的实例后,就不允许修改它的值了。特别注意:String和包装类(Integer,Float...)都是不可变类。
扩展问题1:new String("abc");创建了几个对象?
1个或2个对象。如果常量池中原来有“abc”,那么只创建一个对象;如果常量池中原来没有字符串“abc”,那么就会创建2个对象。
扩展问题2:
String s ="abc";
String ss ="ab"+"c";
System.out.pringln(s==ss);
输出结果为:true
解析:"ab"+"c"在编译时就被转换为“abc”。
扩展问题3:
String s="abc";
char [] ch={'a','b','c'};
System.out.println(s.equals(ch));
输出为:false
解析:S和ch分别为字符串类型和数组类型,所以输出为false。
5、运算符的优先级
(++,--)>(*,/,%)>(+,-)>(<<,>>)>(&)>( | )> && > ||
public static void main(String[] args){
byte a=5;
int b=5;
int c=a>>2+b>>2;
System.out.println(c);
}
//输出是0
public static void main(String[] args){
int a=10,b=4,c=5,d=9;
System.out.println(++a*b+c*--d);
}
//输出是84
6、强制类型转换时的规则有哪些?
1)当对小于int的数据类型(byte,char,short)进行运算时,首先会把这些类型的变量值强制转为int类型,对int类型的值进行运算,最后得到的值也是int类型的。因此如果把2个short类型的值相加,最后得到的结果是int类型,如果需要得到short类型的结果,就必须显式地运算结果转为short类型。
例:short s1=1;s1=s1+1;
编译出错。正确的写法是short s1=1;s1=(short)(s1+1);
例:short s1=1;s1+=1;
编译通过。
2)基本数据类型和boolean类型是不能相互转换的。
3)char类型的数据转为高级类型时,会转换为对应的ASCII码。
例1:
int i=1;
if(i)
System.out.println(i);
编译出错。基本数据类型和boolean类型是不能相互转换的。
例2:
short i = 128;
System.out.println((byte)i);
i对应的二进制为00000000 10000000,由于byte只占一个字节,在强制转换的时候,从前面开始截掉,因此截掉后的值为二进制的10000000,也就是十进制的-128。
7、数组初始化时需要注意的问题
1)数组被创建后会根据数组存放的数据类型默认初始化为对应的初始值,例如,int类型会初始化为0,对象类型会初始化为null。
2)二维数组中,每行元素个数可以不同。
8、如何在main()方法执行前输出“hello world”?
用静态代码块。静态代码块在类加载的初始化阶段就会被调用。
9、Java程序初始化的顺序(对象实例化的过程)
1)父类的静态变量、弗雷德静态代码块(谁在前,谁先初始化)
2)子类的静态变量、子类的静态代码块(谁在前,谁先初始化)
3)父类的非静态变量、父类的非静态代码块(谁在前,谁先初始化)、父类的构造函数
4)子类的非静态变量、子类的非静态代码块(谁在前,谁先初始化)、子类的构造函数
10、构造函数的特点
1)构造函数必须和类名一样(但和类名一样的不一定是构造方法,普通方法也可以和类名同名),并且不能有返回值,返回值也不能为void。
2)构造函数总是伴随着new操作一起调用,并且不能由程序的编写者调用,只能由系统调用。
3)构造函数不能被继承。
4)子类可以通过super()来显示调用父类的构造函数。
11、Switch能否用string做参数?
在Java7之前,switch只能支持byte、short、char、int或者其对应的包装类以及Enum类型。在Java7中,String支持被加上了。
在使用switch时,需要注意另外一个问题,如果和匹配的case情况中省略了break,那么匹配的case值后的所有情况都会执行,而不管case是否匹配,一直遇到break结束。
int x=2;
switch(x){
case 2:
System.out.println(x);
case 3:
System.out.println(x);
case 4:
System.out.println(x);
break;
default:
System.out.println("dddddd");
}
输出结果为:
2
2
2
12、接口和抽象类的区别
1)语法层面上的区别
a. 抽象类可以提供成员方法的实现细节(注:可以只包含非抽象方法),而接口中只能存在public abstract方法,方法默认是public abstract的,但是,Java8中接口可以有default方法;
b. 抽象类中的成员变量可以是各种类型的,二接口中的成员变量只能是public static final类型的;
c. 抽象类可以有静态代码块和静态方法和构造方法;接口中不能含有静态代码块及静态方法以及构造方法。但是,Java8中接口可以有静态方法。
d. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2)设计层面上的区别
a. 抽象层次不同。抽象类是对类的整体抽象,包括属性和行为的抽象。而接口只是对行为的抽象。
b. 跨域不同。抽象类所体现的是一种继承关系,父类和派生类之间必须存在“is-a”关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的,仅仅是实现了接口定义的契约而已,其设计理念是“has-a”的关系(有没有、具备不具备的关系),实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞接口,具备飞的行为,这里我们总不能说鸟和飞机共用一个父类吧!
c. 设计层次不同。对于抽象类而言,它是自上而下来设计的,我们要先知道子类才能抽象出父类,而接口不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类,什么时候怎么实现它一概不知。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
13、Java和C++/C的区别,Java的优点
Java和C/C++的区别:
1)运行过程不同。Java源程序经过编译成字节码文件,然后由JVM解释执行。而C++/C经过编译、链接后生成可执行的二进制代码。因此C/C++的执行速度比Java快。
2)跨平台性。Java可以跨平台,而C++/C不可以跨平台。
3)Java没有指针,C++/C有指针。
4)Java不支持多重继承,但是可以同时实现多个接口来达到类似的目的。C++/C支持多重继承。
5)Java不需要对内存进行管理,有垃圾回收机制。C++/C需要对内存进行显示的管理。
6)Java不支持运算符重载。C++/C支持运算符重载。
7)Java中每个数据类型在不同的平台上所占字节数固定的,而C++/C则不然。
Java的优点:
1)跨平台。Java语言可以“一次编译,到处运行”。跨平台的含义:无论是在Windows平台还是在Linux平台对Java程序进行编译,编译后的程序都可以在其他平台上运行。编译器会把Java代码编译成字节码文件,然后在JVM上解释执行,由于字节码与平台无关,因此,Java可以很好地跨平台执行。
2)垃圾回收机制
3)去掉了C++中难以理解的东西,比如指针和运算符重载。
4)具有较好的安全性。比如Java有数组边界检测,但是C++/C里面没有。
14、同一个 .java文件中是否可以有多个main()方法?
每个类中都可以定义main()方法,但只有用public修饰的类且与文件名相同的类中的main()方法才可以作为整个程序的入口方法。
例子:
//创建一个名为test.java文件
class T{
public static void main(String[] args){
System.out.println("T main");
}
}
public class test{
public static void main(String[] args){
System.out.println("Test main");
}
}
输出:
Test main
15、Java中的类和成员的访问控制
类的访问控制。可以用public和不含public的来修饰。
成员的访问控制。
如果一个类是用public来修饰的,它的成员用第一列的访问修饰符时,在不同范围的类和对象是否有权访问它们。
public(公开的):所有人都能访问;
protected(受保护的):一个包下的能够访问,子类可以访问;
default(缺省的):同包下的能够访问;
private(私有的):只能自身访问,其他人都不能访问;
16、JDK1.7和JDK1.8的区别
接口的默认方法。
Java8 允许我们给接口添加一个非抽象的方法实现,只需要使用default关键字即可,这个特征又叫做扩展方法,Default方法带来的好处是,往接口新增一个Default方法,在街口添加新功能特性,而不破坏现有的实现架构。示例如下:
interface Formula{
double calculate(int a);
default double sqrt(int a){
return Math.sqrt(a);
}
}
Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。
Formula formula = new Formula(){
public double calculate(int a){
return sqrt(a*100);
}
};
formula.calculate(100); //100.0
formula.sqrt(16); //4.0
17、String中的“+”操作是怎么回事?
情况1:
String a="ab";
String bb="b";
String b="a"+bb;
System.out.println((a==b));
“String+变量”因为编译时无法进行优化,所以这一条语句的操作是在运行时进行的,且会产生新的对象,而不是直接从JVM的string池中获取。
Java中String的+运算符编译后其实是转换成了这样的代码:
String b= new StringBuffer().append("a").append(bb).toString();
其中,StringBuffer的toString方法,使用new String(...);来构造一个String对象。
public String toString(){ //StringBuffer的toString方法
return new String(value,0,count);
}
情况2:
一个特殊的例子:
String str="this is only a"+"simple"+"test"; //"+"连接的都是字符串变量
StringBuffer builder = new StringBuffer("This is only a").append("simple").append("test");
你会很惊讶地发现,生成str对象的速度简直太快了,而这个时候StringBuffer居然速度上根本一点都不占优势。其实这是JVM的一个把戏,实际上:
String str = "this is only a"+" simple"+ "test";
其实就是:
String str = "this is onlt a simple test";
所以不需要太多的时间,但大家这里要注意的是,如果你的字符串是来自另外的String对象的话,速度就没那么快了,譬如:
String str2="this is only a";
String str3="simple";
String str4="test";
String str1=str2+str3+str4; //"+"连接的是字符串变量
这时候JVM会规规矩矩地按照原来的方式去做。
18、创建虚引用的时候,构造方法传入一个ReferenceQueue,作用是什么?
虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
19、栈溢出的原因和解决方法
原因:
1)大量的递归调用,在不断的压栈过程中,造成栈容量超过而导致溢出。
2)由于分配了过大的局部变量。
解决方法:
1)用栈把递归换成非递归。
2)使用静态对象替代非静态局部对象。
在递归函数设计中,可以使用静态对象替代非静态局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放非静态对象的开销,而且静态对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。
3)增加堆栈的大小。
20、HashMap的加载因子的作用
加载因子是表示Hash表中元素的填满程度。若加载因子越大,填满的元素越多,好处是,空间利用率高了,但冲突的机会加大了,增加查询数据的时间开销。反之,加载因子越小,填满的元素越少,好处是,冲突的机会减小了,会提高数据查询的性能,但空间浪费多了。特别地,JDK1.8中对HashMap进行了增强,如果一个桶上的节点数量过多,链表+数组的结构就会转换为红黑树。
21、HashMap中的key可以是任意对象吗?(Set中元素的内容可以改变吗?)
可变对象和不可变对象都可以。可变对象是指创建后自身状态能改变的对象。换句话说,可变对象是该对象在创建后它的哈希值(由类的hashcode()方法可以得出哈希值)可能被改变。
如果可变对象在HashMap中被用作key,当对象中的属性改变时,则对象HashCode也可能进行相应的改变,这将导致下次无法查找到已存在Map中的数据;或者当想删除被改变对象的时候,由于找不到该对象,无法删除,会造成内存泄漏。所以当hashcode()重写的时候,最好不能依赖该类的易变属性,这样就可以保证成员变量改变的时候,该对象的哈希值不变。
22、Java是如何实现跨平台的?
Java跨平台的是指是虚拟机的跨平台。JVM也是一个软件,不同的平台有不同的版本。我们编写的Java源码,编译后会生成一种.class文件,成为字节码文件。Java虚拟机就是负责将字节码文件翻译成特定平台下的机器码然后运行。也就是说,只要在不同平台上安装对应的JVM,就可以运行字节码文件,运行我们编写的Java程序。