有趣的Java

以下题目大多来源于关于Java基础的一些经典题目,旨在帮助自己和他人对Java基础知识点记得更加牢固

持续更新中......

  • Java中是不是所有的继承只能为单继承?比如说类只能但继承一个类,接口只能单继承一个接口?

 

有趣的Java_第1张图片

答案:在Java中类只能继承一个类,但是接口可以继承多个接口 。但是接口无法多重继承这些接口:这些接口中含有相同的default函数(函数签名一样)(只限于default方法,如果方法前是static,仍可以多重继承)(对于接口中的方法,abstract,default,static三者只能同时出现一个)。如下:

有趣的Java_第2张图片

 更详细的内容,跳转:https://blog.csdn.net/smart_ferry/article/details/84677984

  • Math.ceil() 和 Math.floor() 你真的了解吗???看以下代码的输出:

public class Main {
	public static void main(String[] args) {
		double d1 = -0.5;
		System.out.println("ceil d1=" + Math.ceil(d1));
		System.out.println("floor d1=" + Math.floor(d1));
	}
}

答案: ceil d1=-0.0 floor d1=-1.0  不多解释,看源码怎么说:

有趣的Java_第3张图片

  • 静态代码块中的变量和静态变量的区别。看看以下代码会输出什么:

public class Main {
	static {
		int x = 5;
	}
	static int x, y;
	public static void main(String[] args) {
		x--;
		myMethod();
		System.out.println(x + y + ++x);
	}
	public static void myMethod() {
		y = x++ + ++x;
	}
}

答案:3   解释:首先,静态代码块中的x与静态变量x没有半毛钱关系!!!静态代码块中的x只是一个局部变量,生命周期为该静态代码块内。知道这点之后,接下来的先++和后++就不用再多说了

  • 你真的了解int和boolean吗???看如下代码的输出结果:

public class Main {
	public static void main(String[] args) {
		int x=3;
        int y=1;
        if(x=y)
            System.out.println("Not equal");
        else
            System.out.println("Equal");
	}
}

答案:编译错误。错误信息:无法从int转为boolean 

  • Java中的char你真的了解吗???char能不能存储一个汉字???以下代码能不能输出:

public class Main {
	static char c = '成';
	public static void main(String[] args) {
		System.out.println(c);
	}
}

答案:正确输出。因为在Java中,char存储的为unicode码,不仅可以存储ascII码,汉字也可以。另:Java中还可以使用中文作为变量名


----------------------------------分隔区    2019.01.25----------------------------------


  • 对于byte型数据的运算和取值范围,你真的了解吗???如下代码会输出什么:

public class Main {
	public static void main(String[] args) {
		A a = new A();
		a.test();
	}
}

class A {
	public void add(Byte b) {
		b = b++;
	}

	public void test() {
		Byte a = 127;
		Byte b = 127;
		add(++a);
		System.out.print(a + " ");
		add(b);
		System.out.print(b + "");
	}
}

答案:-128 127  解释:byte型变量占一个字节,8位。a = 127 = 01111111(127的补码形式) 此时的++a意为a加上一个byte形式的1,即a = 01111111 + 00000001 = 10000000(符号位也参与加法运算),请注意,此时a变为了一个负数(因为最左边的符号位变成了1)。而10000000为这个负数的补码,现在要做的是从一个负数的补码求负数的原码,步骤如下:首先求这个补码的反码,每个位都取反,包括符号位(与已知负数的原码求负数的补码步骤稍微不同),得到反码为01111111,然后再加1,若进位到了最高为,则最高位也需要加1,结果为10000000,此结果为负数对应的绝对值的大小,即为128,所以该负数为 -128    而对于add()方法传进去的值,是为值传递,改变的是局部变量b(add方法中的形式参数)的大小,传进去的a值并没有变化。

  • 对于static方法,你真的了解吗???如下代码是否会正常输出:

public class Main {
	private static void testMethod() {
		System.out.println("testMethod");
	}

	public static void main(String[] args) {
		((Main) null).testMethod();
		System.out.println("ok");
	}
}

 答案:正常输出 testMethod ok   解释:testMethod方法为static方法,在Java虚拟机对 ((Main) null).testMethod(); 编译时会将其变成 testMethod(); 省略 Main. 是因为调用者和被调用者在同一个类中。  查看反编译代码如下:

有趣的Java_第4张图片

 如果testMethod方法不是static方法的话,则会报空指针异常。查看反编译代码如下:

有趣的Java_第5张图片

 另:测试testMethod方法和main方法不在同一个类的情况,源代码如下:

public class Main {
	public static void main(String[] args) {
		((A) null).testMethod();
		System.out.println("ok");
	}
}
class A{
	public static void testMethod() {
		System.out.println("testMethod");
	}
}

查看反编译代码如下:

有趣的Java_第6张图片

  •  对于取值运算符 % 你真的了解吗???如下代码会输出什么:

public class Main {
	public static void main(String[] args) {
		System.out.println(100 % 3);
		System.out.println(100 % 3.0); 
	}
}

答案:1  1.0  解释: 第一个为1没得说,而第二个输出语句为1.0其实也好理解(这里和C语言的%规则是不同的),想想其他的四个加减乘除运算符,如果有一个操作数为double,另一个操作数为int,则结果也是double。   另:100 % 3   和  100 % 3.0  已经在Java虚拟机编译源码的时候给计算出来了。查看反编译代码如下:

有趣的Java_第7张图片

  •  对于字符串常量你真的了解吗???如下代码的输出结果是什么:

public class Main {
	private static final String MESSAGE = "taobao";
	public static void main(String[] args) {
		String a = "tao" + "bao";
		String b = "tao";
		String c = "bao";
		System.out.println(a == MESSAGE);
		System.out.println((b + c) == MESSAGE);
	}
}

答案:true false   解释:首先执行第一条static语句为MESSAGE赋值,该字符串赋值未使用new 关键字,而是使用字符串常量赋值,首先检查字符串常量池中是否有"taobao"这个字符串,发现没有,那么"taobao"这个字符串常量将被放入堆中的字符串常量池中,而变量MESSAGE存放在方法区中(由于Message由static和final修饰),指向字符串常量池中对应的字符串。接着执行 String a = "tao" + "bao",在这一步中,编译器会直接将"taobao"这个字符串赋值给变量a,由于a也是通过字符串常量来赋值,那么也会先去字符串常量池中查看是否有该字符串常量,发现已存在,那么返回字符串常量池中的该字符串的引用给a。那么此时a所指向的地址和MESSAGE所指向的地址是同一个(注意是它们所指向的地址,不是变量本身所在的地址。变量a是位于栈中的,因为变量a是在方法中创建的,而方法是位于栈内)。所以第一个判断语句就是为真。 而对于第二个判断语句,编译器并不会把b+c直接编译陈"taobao",因为编译器不知道在进行b+c之前b,c是否还会变化。而b+c所对应的操作是new StringBuild(),然后append(b),再append(c),最后返回StringBuild的toString(),而在StringBuild中的源码中,调用toString()时,是使用了new关键字的。所以(b + c)指向的应该是这么一个新对象,这个对象在堆中,但是不在字符串常量池中 。所以第二个判断应该为false   查看反编译代码如下:

有趣的Java_第8张图片

从反编译得到的代码可以看出,a确实被直接赋值了"taobao",而第一个判断中的MESSGAE直接编译成了它所指向地址的内容,即"taobao",这是因为MESSAGE是由final修饰,表示MESSAGE所指向的地址所存储的字符串不可变。但是如果MESSAGE只是由static修饰,那么第一个判断中MESSAGE并不会被取代为"taobao".

其实关于变量指向对象,个人觉得可以这么理解:每一个变量都存储着一个值,这个值可以是该变量的值,也可以是它所引用对象的所在的地址。如果该变量是一个引用变量,则这个值为它所引用的对象的地址,即这个变量指向这个地址

  • 你以为final就只是上面这么一点点吗???以下代码是会报错还是正确输出:

import java.util.Arrays;

public class Main {
	private static final char[] a = {'a','b','c'};
	public static void main(String[] args) {
		System.out.println(Arrays.toString(a));
		a[0] = 'd';
		System.out.println(Arrays.toString(a));
	}
}

答案:[a, b, c]  [d, b, c]  解释:变量a有一个值,这个值为对象{'a','b','c'}的地址,即变量a指向这个对象,这个对象内部有三个连续小块(数组空间连续分配),每个小块里面有一个值,各个值为的是'a','b','c'的地址,即各个小块指向'a','b','c'。而现在定义的是变量a为final型,即它的内容为final型。根据之前分析,它的内容是对象数组的地址,即这个对象数组的地址不能变化。但是这个对象数组里面的小块所指向的字符是可以变化的。

  • 再来一个final,看看是否理解。代码如下,是否会编译成功并正确输出:

public class Main {
	private static final String S = "HELLO";
	private static final StringBuilder SB = new StringBuilder("HELLo");
	public static void main(String[] args) {
		S += "Java";
		SB.append("Java");
		System.out.println(S);
		System.out.println(SB.toString());
	}
}

答案:S += "Java"; 该语句编译出错 ,注释后正确输出   解释:S 为一个变量,它的内容为"HELLO"字符串所在的地址,因此即为S指向"HELLO"。S为final,意为它的内容不可变,即它所指向的地址不可变,即S只能指向这个字符串。但是这个地址的内容可变。但又由于这个地址存放的是String,而String是不可变的,因此导致S指向的地址不可变,该地址的内容(字符串)也不可变    再看SB变量,它指向一个StringBuild对象,SB为final型,意即为SB只能指向这个StringBuild对象,但是StringBuild对象的内容可变。由于StringBuild对象确实可变,所以编译成功并成功运行输出

  • 其实,当我们对一个变量调用toString()(不重写toString())时,输出的是它的内容。那它的内容是什么呢?想想我们输出的是什么,是一个地址吧,而这个地址就是它所指对象所在的地址


----------------------------------------   分割区  2019.01.30 -----------------------------------------------------


  • super关键字在哪三种情况需要用到???用法如何???如下代码输出结果为什么:

public class Main {

	public static void main(String[] args) {
		new C();
	}
}
class P{
	int v = 1;
	public P(int v){
		this.v = v;
	}
	void hello() {
		System.out.println("hello");
	}
}
class C extends P{
//	int v = 4;
	public C(){
		super(2);
		v = 3; 
		System.out.println(super.v);
		System.out.println(v);
		super.hello();
	}
}

答案: 2 3 hello  解释:在类C的构造函数中,首先调用父类P的带一个参数的构造函数,为父类的成员变量v赋值为2;下一条语句的v=3是作用在类C的自身新建的成员v之上,由于此时类 C的成员v为其父类P的成员v(C并没有新建成员v,所以this.v是指向其父类的v),所以修改的为其父类P的成员v的值(即子类有就用自己的,子类没有就用父类的);之后的输出也是一样的分析

  • 在上例中,若把类C中的注释去掉,那么输出结果又会是什么???

答案: 2  3  hello 解释:super(2)初始化的是父类的成员变量v,而此时类C自己也有一个成员变量为v。因此C可以通过关键字super访问父类的成员变量,通过关键字this或者不使用关键字访问的是自己另外建的变量v

  • 线程里的run和start你真的了解吗???如下代码是否会输出:

public class Main extends Thread{

	public static void main(String[] args) {
		new Main().run();
	}
	public void start() {
		for (int i = 0; i < 10; i++) {
			System.out.print(i + " ");
		}
	}
}

答案:不报错,无任何输出   解释:正常开启线程是重新Thread的run方法,再调用start方法开启线程,在这里刚好相反,但是程序也不会报错。查看run方法和target变量值如下:

有趣的Java_第9张图片

可以看到,我们并没有对target做任何初始化赋值,因此它等于null,所以run方法将会直接返回 

  • 你对byte和int之间的转换真的了解吗???如下代码那几行编译出错:

public class Main extends Thread{
	public static void main(String[] args) {
		byte a = 2,b = 4,c;
		int d = 100;
		b = 100;
		b = d;
		c = 2 * 4;
		c = a * b;
		c = 100 * 2;
	}
}

答案:6、8、9行编译出错  解释: 第5行:b = 100 编译器会把100转换为b的类型(byte),因为100在byte的取值范围之内,所以转换成功;而在第9行,c = 100 * 2中,200不能转成byte(大于byte的最大值127),所以第9行会提示编译出错;而第7行等号右边的结果为8,在byte范围之内,可以转换;第6行尝试直接把一个int型的值赋给byte型变量,由高精度赋值给低精度,需要强制转换,因此错误;第8行等号右边为两个int型变量相乘,结果为int,需要强制转换

总结:当遇到char、byte、short、int型变量(注意是变量,不是字面值)进行四则运算和求余时,其结果为int型(这些参与运算的变量都会转为int型);而遇到字面值进行四则运算时,编译器会查看字面值操作后的结果是否在等号左边变量类型的范围之内,如果在则可成功赋值

  • 外部类只能被public、abstract、final或者无修饰符修饰


 

---------------------------------------------分割区 2019.01.31-----------------------------------------------------


  • null只能赋值给引用类型变量(包括基本数据类型的包装类),不能赋值给基本数据类型变量 

  • this关键字不能用在static方法中

  • 对于包装类的equals和==你真的了解吗???如下代码会输出什么:

public class Main{
	public static void main(String args[]) {
		Integer a = 1;
		Integer b = 2;
		Integer c = 3;
		Integer d = 3;
		Integer e = 321;
		Integer f = 321;
		Long g = 3L;
		System.out.println(c == d);//1
		System.out.println(e == f);//2
		System.out.println(c == (a + b));//3
		System.out.println(c.equals(a + b));//4
		System.out.println(g == (a + b));//5
		System.out.println(g == c);//6
		System.out.println(g.equals(a + b));//7
	}
}

答案: 6编译出错,因为 == 两边只能是相同类型,而此时等号两边为Long和Integer,包装类在不遇到算术运算的情况下不会自动拆箱。将6注释后,输出结果为:true false true true true false     

解释:包装类的“==”运算在不遇到算术运算符的情况下不会自动拆箱,只要“==”一边存在算术运算符,则两边都会自动拆箱,并且低精度值会自动向上转为高精度值包装类的equals方法不处理数据转型的关系(这个待会看下面的Long类的equals方法的实现)两个Integer变量值相等且在-128~127之间(即byte的取值范围,为什么是byte的取值范围呢???),则两者使用“==”返回结果为true,否则返回为false

Long类的equals方法定义:

有趣的Java_第10张图片

可以看到,equals方法是先判断obj是否为Long的一个实例(因为Long为final型,不可被继承,无子类),即其是否为Long类型 ,如果不是,则直接返回false


----------------------------------------------------- 2019.02.05 ---------------------------------------------------- 


  • 对于short、byte和char你真的了解吗??? 如下代码是否会编译通过:

public class Main{
	public static void main(String args[]) {
		short a = 49;//1
		byte b = a;//2
		char c = a;//3
	}
}

答案: 语句2和3编译出错   解释:short、byte、char是相同级别,相互之间只能进行强制转换 

  • 赋值运算“=”你真的了解吗???如下代码是否会编译通过并输出什么:

public class Main{
	public static void main(String args[]) {
		boolean flag = true;
		if(flag = false) {
			System.out.println("true");
		}else {
			System.out.println("false");
		}
	}
}

答案:编译通过并输出 false   解释:对于本例中的if语句中赋值运算  flag = false ,其编译后为 (flag = 0) != 0. 查看反编译代码如下:

有趣的Java_第11张图片

总结:可记为赋值运算会默认返回一个值,该值为等号的右值

  •  int和double之间的自动转换,你真的清楚了吗???如下代码的输出结果是什么:

public class Main{
	public static void main(String args[]) {
		int a = 5;
		System.out.println(a < 5 ? 10.9:9);
	}
}

答案:9.0  解释:一个是10.9(double),一个是9 (int),编译时int自动转为double。反编译代码如下:

有趣的Java_第12张图片

  •  对于char和int之间的转换,你真的了解了吗???如下代码输出结果是什么:

public class Main{
	public static void main(String[] args) {
		char x = 'x';
		int i = 10;
		System.out.println(false ? i : x);//1
		System.out.println(false ? 10 : x);//2
		System.out.println(false ? 100000 : x);//3
	}
}

答案:120 x 120   解释: 三个输出语句都是输出x    i 是一个变量,为int型;x是一个变量,为char型。因此第一个输出中,x会被转型为int,即为120   而对于第二和第三个输出,Java编程规范中提到:当后两个表达式有一个是常量,一个是变量(记类型为T,该例为char)时,若该常量可以被类型T所表示,那么输出结果是T类型;否则输出结果是常量类型

  •  

 

你可能感兴趣的:(Java)