java基础中常见的面试题

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

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

  • 父类的静态代码块

  • 子类的静态代码块

  • 父类的初始化块

  • 父类的构造器

  • 子类的初始化块

  • 子类的构造器

在这里插入图片描述

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对象的地址。内存分析图如下:

在这里插入图片描述

第六题

/* * 
* +号拼接:
* 左右两边操作数如果是字面值,则直接拼接之后再在常量池开空间
* 如果左右两边操作数是变量,则通过常量池重新分配空间存储变量地址
*/
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
com.mage.interview.In$1@15db9742

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

第十二题

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对象。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值