欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java基础中常见的面试题

程序员文章站 2024-03-23 14:59:04
...

java基础中常见的面试题

三目运算符中的常见面试题

第一题

public class Test01 {
	public static void main(String[] args) {
		double num = 90>100?90.0:100;
		System.out.println(num);
	}
}

输出:

100.0

解析: 表达式中类型会自动提升。

第二题

public class Test01 {
	public static void main(String[] args){
		// 判定一个数字奇数还是偶数
		int num = -1;
		// 被除数-被除数/除数*除数
		String str = num%2==1?"奇数":"偶数";
		System.out.println(str);
		}
}

输出:

偶数

解析: java中的取余运算遵循a%b=a-(a/b)*b 的规则,例如:

5%3=5-(5/3)*3=2 
5%-3=5-(5/-3)*-3=2 
-5%3=-5-(-5/3)*3=-2 
-5%-3=-5-(-5/-3)*-3=-2 

如果操作数中有浮点数则采用的规则为a%b=a-(b*q),这里q=int(a/b) ,例如:

5.2%3.1=5.2-1*3.1=2.1 
5.2%-3.1=5.2-(-1)*(-3.1)=2.1 
-5.2%3.1=-5.1-(-1)*3.1=-2.1 
-5.2%-3.1=-5.1-(-1)*(-3.1)=-2.1 

类的加载顺序

第三题

package com.mage.interview;

public class Test02 {
	public static void main(String[] args) {
		new S2().m();
	}
}

class F {
	static {
		System.out.println("F static");
	}
	{
		System.out.println("F init");
	}

	public F() {
		System.out.println("F construct");
	}
}

class S1 extends F {
	static {
		System.out.println("S1 static");
	}
	{
		System.out.println("S1 init");
	}

	public S1() {
		System.out.println("S1 construct");
	}
}

class S2 extends F {
	static {
		System.out.println("S2 static");
	}
	{
		System.out.println("S2 init");
	}

	public S2() {
		System.out.println("S2 construct");
	}

	public void m() {
		new S1();
	}
}

输出:

F static
S2 static
F init
F construct
S2 init
S2 construct
S1 static
F init
F construct
S1 init
S1 construct

解析: 对象被创建时,类的加载顺序是:

  • 父类的静态代码块

  • 子类的静态代码块

  • 父类的初始化块

  • 父类的构造器

  • 子类的初始化块

  • 子类的构造器

java基础中常见的面试题

String

第四题

//由于String不可变 所以经过方法不会发生改变
public class Test03 {
	public static void main(String[] args) {
		String str = "123";
		System.out.println(str);
		change(str);
		System.out.println(str);
	}

	public static void change(String str) {
		str = "234";
	}
}

输出:

123
123

解析: 由于String不可变 所以经过方法不会发生改变。

第五题

//intern 获取到的是当前String对象的地址
public class Test04 {
	public static void main(String[] args) {
		String s1 = "Cat";
		String s2 = "Cat";
		String s3 = new String("Cat");
		System.out.println("s1 == s2 :" + (s1 == s2));
		System.out.println("s1 == s3 :" + (s1 == s3));
		String s4 = s1.intern();
		System.out.println("s1 == s4 :" + (s1 == s4));
		System.out.println("s3 == s4 :" + (s4 == s3));
	}
}

输出:

s1 == s2 :true
s1 == s3 :false
s1 == s4 :true
s3 == s4 :false

解析: String s1 = "Cat"; String直接创建变量时,所创建的变量的值是存放在常量池中的,再一次创建变量赋给相同的值时直接在常量池中寻找该值,String s3 = new String("Cat"); 通过new创建对象会在堆中开辟空间,然后将常量池中该值的地址存放在该开辟的空间中。String s4 = s1.intern(); intern 获取到的是当前String对象的地址。内存分析图如下:

java基础中常见的面试题

第六题

/* * 
* +号拼接:
* 左右两边操作数如果是字面值,则直接拼接之后再在常量池开空间
* 如果左右两边操作数是变量,则通过常量池重新分配空间存储变量地址
*/
public class Test05 {
	public static void main(String[] args) {
		String s1 = "Cat";
		String s2 = "Dog";
		final String s3 = s1 + s2;// append
		String s4 = "CatDog";
		String s5 = "Cat" + "Dog";// s5 = "CatDog"
		System.out.println(s3 == s4);
		System.out.println(s4 == s5);
	}
}

输出:

false
true

解析: 用+号进行字符串拼接:

  • 左右两边操作数如果是字面值,则直接拼接之后再在常量池开空间。
  • 如果左右两边操作数是变量,则通过常量池重新分配空间,该空间存储变量地址。

类加载

一个类何时被加载:

  • 主动使用
    • 创建一类的实例 new Person();
    • 访问某个类、接口中的静态变量,或者对于静态变量做读写;
    • 调用类中的静态方法;
    • 反射 (Class.forName(“xxx.xxx.xxx”));
    • 初始化子类 父类会被加载。
    • Java虚拟机标明的启动器类 (Test.class(main)|Person.class|Student.class,此时Test就是启动器类).
  • 被动使用
    • 引用常量不会导致该类发生初始化[常量它是在编译器确定的]
    • 通过数组定义类的引用,不会触发该类的初始化
    • 通过子类引用父类的静态变量,不会导致子类初始化。

第七题

package com.mage.test;

/*
 *   	类加载:
 * 			 连接
 * 				验证(字节码文件的验证)
 * 				准备  将静态内容做初始化  
 * 				解析(将符号引用 变为直接引用)
 * 			  初始化
 * 				给静态内容赋值
 *   
 */
public class Test06 {
	public static void main(String[] args) {
		Singleton s1 = Singleton.getInstance();
		System.out.println(s1.count1);
		System.out.println(s1.count2);
	}
}
class Singleton{
	//准备阶段  null 0 0
    private static Singleton singleton = new Singleton();
	public static int count1;
	public static int count2 = 0;
	private Singleton(){
		count1++;
		count2++;
	}
	public static Singleton getInstance(){
		return singleton;
	}
}

输出:

1

0

解析: Singleton s1 = Singleton.getInstance(); 调用了类中的静态方法,类被加载,静态内容也被加载,利用private static Singleton singleton = new Singleton(); 创建对象时加载构造方法,此时count1=1,count2=2,然后加载public static int count1;public static int count2 = 0; 将count2=0.

第八题

public class Test07{
	public static void main(String[] args) {
		T[] ts = new T[10];
		System.out.println(ts.length);
	}
}
class T{
	static {
		System.out.println("我被执行了");
	}
}

输出:

10

解析: 通过数组定义类的引用,不会触发该类的初始化。

第九题

public class Test08 {
	public static void main(String[] args) {
		System.out.println(Son.num);
	}
}
class Father{
	static int num = 20;
	static {
		System.out.println("F 我被执行了");
	}
}
class Son extends Father{
	static {
		System.out.println("S 我被执行了");
	}
}

输出:

F 我被执行了
20

解析: 通过子类引用父类的静态变量,不会导致子类初始化。

第十题

public class Test09 {
	public static void main(String[] args) {
		System.out.println(T2.num);
	}
}
class T2{
	static final int num = 20;
	static {
		System.out.println("F 我被执行了");
	}
}

输出:

20

解析: 引用常量不会导致该类发生初始化[常量它是在编译器确定的],如果static int num=(int)Math.random()*20 该常量是一个变化的值,这种情况下类会被初始化,静态内容被加载。

第十一题

public class Test10 {
	public static void main(String[] args) {
		System.out.println(new T3().num);
		System.out.println(T3.t);
	}
}
class T3 implements In{
	static final int num = 20;
	static {
		System.out.println("F 我被执行了");
	}
}
interface In{
	Test05 t = new Test05() {
		{
			System.out.println("1");
		}
	};
}

输出:

F 我被执行了
20
1
aaa@qq.com

解析: 子类被引用,父类接口没有被调用时,父类接口不会被加载,只有当接口被调用时接口中的内容才会被加载。

第十二题

public class Test11 {
	public static void main(String[] args) {
		System.out.println(Tns.num);
		
	}
}
interface Tns extends In1{
	static int num = 20;
}
interface In1{
	Test06 t = new Test06() {
		{
			System.out.println("1");
		}
	};
	
	
}

输出:

20

解析: 只是调用了子类的引用并没有使用接口中的内容,所以接口内容不会被加载。

类接口没有被调用时,父类接口不会被加载,只有当接口被调用时接口中的内容才会被加载。

第十二题

public class Test11 {
	public static void main(String[] args) {
		System.out.println(Tns.num);
		
	}
}
interface Tns extends In1{
	static int num = 20;
}
interface In1{
	Test06 t = new Test06() {
		{
			System.out.println("1");
		}
	};
	
	
}

输出:

20

解析: 只是调用了子类的引用并没有使用接口中的内容,所以接口内容不会被加载。

包装类

第十三题

public class Text02 {
	public static void main(String[] args) {
		Integer io1 = 59;
		int io2 = 59;
		Integer io3 = Integer.valueOf(59);
		Integer io4 = new Integer(59);
		System.out.println(io1==io2);
		System.out.println(io1==io3);
		System.out.println(io3==io4);
		System.out.println(io2==io4);
		
		
		
		System.out.println(io1.equals(io2));
		System.out.println(io1.equals(io3));
		System.out.println(io3.equals(io4));
		System.out.println(io4.equals(io2));
	}
}

输出:

true
true
false
true
true
true
true
true

解析:

第十三题

package eclipse;
 
public class Test_Integer {
 
   public static void main(String[] args) {
 
     Integer i1 = new Integer(97);   
     Integer i2 = new Integer(97); 
     System.out.println(i1 == i2);
     System.out.println(i1.equals(i2));
     System.out.println("----------------");
 
     Integer i3 = new Integer(197);  
     Integer i4 = new Integer(197);
     System.out.println(i3 == i4);
     System.out.println(i3.equals(i4));
     System.out.println("----------------");
 
     Integer i5 = 97;   
     Integer i6 = 97; 
     System.out.println(i5 == i6);
     System.out.println(i5.equals(i6));
     System.out.println("----------------");
 
     Integer i7 = 197;  
     Integer i8 = 197;
     System.out.println(i7 == i8);
     System.out.println(i7.equals(i8));
 
   }
}

输出:

false

true

----------------

false

true

----------------

true

true

----------------

false

true

解析: == 这个符号既可以比较基本数据类型和引用数据类型,如果是基本数据类型,比较的是值相同,如果是引用数据类型,比较的是对象的内存地址是否相同。利用equals方法比较的是值是否相同,因为Integer类里面重写了Object类的equals方法。

​ 所以,上面的equals方法打印输出都是true,这个很简单,没有疑问。然后来看,前面两组里面的== 判断,由于 == 是比较内存地址,我们看到s1 ,s2 ,s3 ,s4都使用了new关键字,所以这两组里面的 == 比较也是不相等,内存地址不同。
最后来看后两组,同样是自动包装,为什么97就输出true,而197就不相等。我们这里先抛出个结论:在Java中,byte的取值范围是-128到127之间。在自动包装和拆箱中,如果变量取值范围在byte的范围内,在自动包装和拆装过程中就不新创建对象(堆内存);如果范围不在byte取值范围内,就会新创建Integer对象。