- Java 面试题2

文章目录


一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?

可以有多个类,但只能有一个 public 的类,并且 public 的类名必须与文件名一致;

  • Java程序是从一个public类的main函数开始执行的,只能有一个public类是为了给类装载器提供方便;
  • 一个public类只能定义在以它的类名为文件名的文件中;
  • 每个编译文件都只有一个public类,因为每个编译文件都只能有一个公共接口,用public来表现;若有一个以上的public类,编译器就会报错;
  • 将程序中包含 main 方法的类名提供给字节码解释器, 以便启动这个程序:

在 JAVA 中如何跳出当前的多重嵌套循环

(1)使用标号:
在最外层循环语句前定义标号,然后再里层循环体的代码中使用带有标号的break语句,跳出外层循环;
在这里插入图片描述

(2)让外层循环 条件表达式的结果 可以受到里层循环体代码的控制:
在这里插入图片描述


switch 语句能否作用在 byte/long/String 上?

switch(expression)中,expression 只能是一个整数表达式、枚举常量;而整数表达式可以是int基本类型或Integer包装类型;

byte、short、char 可以隐含转换为int,所以这些类型以及其包装类型可以作为switch的表达式;

long、String 类型不符合switch的语法规定,且不能被隐式转换成int类型,所以不能作用于switch语句中;


short s1 = 1; s1 = s1 + 1;对错? short s1 = 1; s1 += 1;对错?

  • short s1 = 1; s1 = s1 + 1;:编译错误!
    s1+1运算时会发生自动类型提升,所以计算结果是int型,再赋值给short类型的s1时,编译器会报错:Type mismatch: cannot convert from int to short;需要强制转换类型;

  • short s1 = 1; s1 += 1;:可以正确编译;
    因为 += 是 java 语言规定的运算符,java 编译器会对它进行特殊处理;


char 型变量中能不能存贮一个中文汉字?为什么?

可以;

char型变量是用来存储Unicode编码的字符的,而Unicode编码字符集中包含了汉字,所以char型变量中可以存储汉字;

但是,若某个特殊的汉字没有被包含在Unicode编码字符集中,那么这个汉字就不能用char型变量来存储;

Unicode编码占用两个字节,所以char型的变量也占两个字节;


用最有效率的方法算出 2 乘以 8 等于几?

2<<8

将一个数左移n位,相当于这个数乘以2的n次方;
程序中所有的数在计算机中都是以二进制的形式存储的,位运算就是直接对整数在内存中的二进制位进行操作;


Integer 与 int 的区别

int是Java的基本数据类型, Integer是Java提供的对int的封装类型;
int默认值是0,integer默认值是null;
integer可以区分0和未赋值的区别,而int无法表示未赋值的情况;
比如想要表达考试成绩为0和没参加考试的情况,只能使用Integer;


super.getClass().getName()输出结果

public class Test extends Date{
    public static void main(String [] args) {
        new Test().t();
    }
    public void t() {
    	System.out.println(super.getClass().getName());    
    	System.out.println(super.getClass().getSuperclass().getName());  
    } 
}

第一个输出:Test
第二个输出:父类Date

在 test 方法中,直接调用 getClass().getName()方法,返回的是 Test 类名;
由于 getClass()在 Object 类中定义成了final,子类不能覆盖该方法,所以,在test 方法中调用 getClass().getName()方法,其实就是在调用从父类继承的 getClass()方法,等效于调用 super.getClass().getName()方法,所以,super.getClass().getName()方法返回的也应该是 Test;
如果想得到父类的名称,应该用如下代码:getClass().getSuperClass().getName();


String s = "Hello";s = s + " world!";这两行代码执行后,原始的 String 对象中的内容到底变了没有?

没有;
因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s 原先指向一个 String 对象,内容是 “Hello”,然后我们对 s 进行了+操作,这时,s 不指向原来那个对象了, 而指向了另一个 String 对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是 s 这个引用变量不再指向它了;


String s = new String("xyz");创建了几个 String Object?二者之间有什么区别?

两个;

首先会创建一个"xyz"对象防在字符串常量池中;然后通过new创建一个String类型的对象在堆内存中,


数组没有length()这个方法,有length 的属性;String 有 length()方法


下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";

1个;

String s1 = "a"; 
String s2 = s1 + "b"; 
String s3 = "a" + "b";
System.out.println(s2 == "ab");    false 
System.out.println(s3 == "ab");    true

javac编译会将字符串常量直接相加的表达式进行优化,直接在编译的时候将+号去掉,将表达式编译成一个这些常量直接相加的结果;不用等到运行时进行加法运算处理;

所以上面的语句在经过编译器在编译时优化之后,相当于直接定义了一个字符串"zbcde",所以只创建了一个String类型的对象放在字符串常量池中;

s1 = s1 + "b";相当于执行了s1 = new String("ab");


try {}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code会不会被执行?什么时候被执行?在 return 前还是后?

不是在return之前执行,可以说是在return中间执行;

public class test extends Date{
    public static void main(String [] args) {
    	System.out.println(new test().t());
    }
    public int t() {
    	int x = 1;
    	try {
			return x;
		} finally {
			++x;
		}
    }
}   

输出:1
主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。

public class test extends Date{
    public static void main(String [] args) {
    	System.out.println(new test().t());
    }
    public int t() {
    	try {
			System.out.println("try");
			return 1;
		} finally {
			System.out.println("finally");
			return 2;
		}
    }
}

返回结果是:try, finally, 2
try 中的 return 语句先执行,finally 语句后执行;Return 并不是让函数马上返回,而是 return 语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally 语句后才真正开始返回;
finally 中的代码比 return 和break 语句后执行;


Thread中有run,传入的d 中也有run,为什么调用的是d.run?

因为Thread类中有一个Runnable接口类型的成员变量r,运行run方法时会先判断这个成员变量是不是为空,若为空就调用Thread的run方法,不为空,就调用r的run方法;
public void run(){ if(r!=null) r.run(); }


当一个线程进入一个对象的一个 synchronized 方法后,其它线程是否可进入此对象的其它方法?

1、其他方法前是否加了 synchronized 关键字,如果没加,则能。
2、如果这个方法内部调用了 wait,则可以进入其他 synchronized 方法。
3、如果其他个方法都加了 synchronized 关键字,并且内部没有调用 wait,则不能
4、如果其他方法是 static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是 this;


Collection 框架中实现比较要实现什么接口

comparable/comparator


两个对象值相同(x.equals(y) == true),但却可有不同的 hash code,这句话对不对?

对;
Java规定equals相等的两个对象,hashcode的值一定要相等;
如果对象要存储在HashSet或HashMap中,它们的equals相等,那么hashcode值就必须相等;
但是如果不是保存在HashSet或HashMap中,equals相等的两个对象的hashcode值可以不相等,因为对象的类可以不用重写hashcode方法,这时候hashcode方法返回的是对象的地址值;


TreeSet 里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的 compareTo 方法,还是使用的子类的 compareTo 方法,还是抛异常

当前add方法放入的是哪个对象,就调用那个对象的compareTo方法;至于compareTo方法怎么做,就要看当前这个对象的类中是如何实现这个方法的;


说出一些常用的类,包,接口,请各举 5 个

常用的包:java.langjava.iojava.utiljava.sqljavax.servletorg.hibernate

常用的接口: ListMapServletHttpServletRequestHttpServletResponseSession(Hibernate)HttpSession

常用的类:BufferedReaderBufferedWriterFileReaderFileWirterStringInteger java.util.DateSystemClass

代码查错

1、

abstract class Name { 
	private String name;
	public abstract boolean isStupidName(String name) {}
}

错;abstract方法必须以结尾,并且不能有{}方法体;

2、

public class Something { 
	void doSomething () {
		private String s = ""; 
		int l = s.length();
	}
}

错;局部变量前不能加访问修饰符(public、private等),可以加final;

3、

abstract class Something {
	private abstract String doSomething ();
}

错;抽象方法不能定义为private/final,因为它需要被子类继承;

4、

public class Something {
	public int addOne(final int x) {
 		return ++x;
	}
}

错;x被修饰成final,在方法中就不能再被修改了;

5、

public class Something {
	public static void main(String[] args) { 
		Other o = new Other();
		new Something().addOne(o);
	}
	public void addOne(final Other o) { 
		o.i++;
	}
}
class Other {
	 public int i;
}

对;因为被定义成final的是对象o,它的引用不能改,但是对象里面的内容可以修改;

6、

class Something { 
	int i;
	public void doSomething() {
		 System.out.println("i = "+ i);
	}
}

对;输出 i = 0;,i属于成员变量,成员变量有默认值,int默认值是0;

7、

class Something { 
	final int i;
	public void doSomething() { 
		System.out.println("i = "+ i);
	}
}

错;final定义的变量必须初始化;

8、

public class Something {
	public static void main(String[] args) { 
		Something s = new Something();
		System.out.println("s.doSomething() returns " + doSomething());
	}
	public String doSomething() { 
		return "Do something ...";
	}
}

错;
System.out.println("s.doSomething() returns " + doSomething());改成System.out.println("s.doSomething()returns " + s.doSomething());

9、此处,Something 类的文件名叫 OtherThing.java

class Something {
	private static void main(String[] something_to_do){ 
		System.out.println("Dosomething ...");
	}
}

正确;类名可以和文件名不一样,但是public class的名字必须和文件名相同;

10、

interface A{
	int x = 0;
}
class B{
	int x =1;
}
class C extends B implements A { 
	public void pX(){
		System.out.println(x);
	}
	public static void main(String[] args) { 
		new C().pX();
	}
}

编译时发生错误;
因为两个x都能调用,有调用的不确定性;
对于父类B的成员变量,可以使用super.x来使用;
接口的成员变量默认是public static final的,可以使用A.x来使用;

11、

interface Playable {  void play(); }
interface Bounceable {  void play(); }
interface Rollable extends Playable, Bounceable {
 	Ball ball = new Ball("PingPang");
}
class Ball implements Rollable { 
	private String name;
	public String getName() { 
		return name;
	}
	public Ball(String name) {
 		this.name =name;
	}
	public void play() {
		ball = newBall("Football");  // 这里编译错误;
		System.out.println(ball.getName());
	}
}

编译失败;
play方法里面的ball变量是从接口 Rollable那继承过来的,接口定义的变量默认是public static final,所以Ball ball = new Ball("PingPang");定义的ball不能再修改引用;而在play方法中,ball = newBall("Football");修改的ball的引用;


创建两个线程交替执行

注意:使用的锁应该是同一个;

public class Test {
	public static void main(String[] args) {
		Demo d1 = new Demo(true);
		Demo d2 = new Demo(false);
		Thread t1 = new Thread(d1);
		Thread t2 = new Thread(d2);
		t1.start();
		t2.start();
	}
}
class Demo implements Runnable{
	private static final Object obj = new Object();
	private boolean flag;
	public Demo(boolean flag) {
		this.flag = flag;
	}
	public void run() {
		if(flag) {
			synchronized (obj) {
				for(int i=0; i<100; i+=2) {
					System.out.println("if..." + i);
					obj.notify();
					try {
						obj.wait();
					} catch (InterruptedException e) {
					}
				}
			}
		}else {
			synchronized (obj) {
				for(int i=1; i<=100; i+=2) {
					System.out.println("else......" + i);
					obj.notify();
					try {
						obj.wait();
					} catch (InterruptedException e) {
					}
				}
			}
		}
	}
}

自动类型提升与强制类型转换

在这里插入图片描述

在这里插入图片描述


对两个整数变量的值进行互换 int a=3,b=5;

(1)int c; c=a;a=b;b=c; //使用第三方变量 (实际开发时用,阅读性强)
(2)a=a+b; a=3+5=8 //不使用第三方变量
b=a-b; b=8-5=3
a=a-b; b=8-3=5
注意:这种方式不要用,若两个整数数值过大,会超过int范围,会强制转换,数据会丢失精度
(3)a=a^b; a=3^5; //使用位运算 (面试时用)
b=a^b; b=(3^5)^5=3
a=a^b; a=(3^5)^3=5
原理:一个数两次异或同一个数,不变;


switch~case

在这里插入图片描述


~ 面向对象练习题

interface A{}
class B implements A{
	public String func(){ return "func"; }
}
class test {
	public static void main(String[] args) {
		A a = new B();  // 多态:父类引用指向子类对象;
		// a.func();    // 编译失败:因为a所属的A接口没有定义func方法;
	}
}
// 对于调用非静态方法,编译看等号左边,运行看右边;
// B提升为A,隐藏自己的内容,使用A的内容,A中没有func()方法
class Fu{
	boolean show(char ch){
		System.out.println(ch);
		return true;
	}
}
class test extends Fu {
	public static void main(String[] args) {
		int i = 0;
		Fu f = new test();
		test t = new test();
		for(f.show('A'); f.show('B') && (i<2); f.show('C')){
			i++;
			t.show('D');
		}
	}
	boolean show(char ch){
		System.out.println(ch);
		return false;
	}
}
// > A B
// 1、f.show('A'):f是接口,接口引用指向子类对象,子/父类都有show方法,
//    所以子类方法覆盖父类方法,f调用的是子类的show方法,So输出'A',返回false,
//    false在for循环的第一个位置,没用;
// 2、f.show('B') && (i<2):左边表达式调用子类的show方法,输出'B',返回false,
//    双 & 进行短路计算,整个大的表达式结果为false;for循环条件为假,循环结束;
interface A{}
class B implements A{
	public String test(){ return "yes"; }
}
class test {
	static A get(){ return new B(); }  // 静态方法,返回值是接口A类型;
	public static void main(String[] args) {
		A a = get();
		System.out.println(a.test());
	}
}
// 编译失败;因为A接口中没有定义test方法;
// A a = get(); 相当于 A a = new B(); 接口引用指向子类对象;
class Super{
	int i = 0;
	public Super(String a){
		System.out.println("A");
		i = 1;
	}
	public Super(){
		System.out.println("B");
		i+=2;
	}
}
class test extends Super{
	public test(String a){
		// super();   // 默认访问父类中空参数的构造函数;
		System.out.println("C");
		i+=5;
	}
	public static void main(String[] args) {
		int i = 4;
		Super d = new test("A");
		System.out.println(d.i);
	}
}
// B C 7
// 1、new test("A"):调用本类中带参构造函数,
// 2、子类构造函数先调用父类的无参构造函数:输出B,i=0+2=2;
// 3、子类构造函数输出"C",i=2+5=7;
class TD{
	int y = 6;
	class Inner{
		static int y = 3;
		void show(){ System.out.println(y); }
	}
}
class test {
	public static void main(String[] args) {
		TD.Inner ti = new TD().new Inner();
		ti.show();
	}
}
// 编译失败:非静态的内部类中不能定义静态成员
选择题:写出错误答案错误的原因: class Demo{ int show(int a,int b){return 0;} }
下面哪些函数可以存在与Demo的子类中:
A、public int show(int a,int b){return 0;}   // 可以,覆盖;
B、private int show(int a,int b){return 0;}  // 不可以,子类的权限应该大于父类的权限;
C、private int show(int a,long b){return 0;} // 可以,参数列表的参数类型不完全一样,是子类的特有方法,不是父类方法的覆盖;
D、public short show(int a,int b){return 0;} // 不可以,调用的不确定性;
E、static int show(int a,int b){return 0;}   // 不可以,静态只能覆盖静态;
class Fu{
	int num = 4;
	void show(){ System.out.println("FuShow"); }
}
class Zi extends Fu{
	int num = 5;
	void show(){ System.out.println("ZiShow"); }
}
class test {
	public static void main(String[] args) {
		Fu f = new Zi();
		Zi z = new Zi();
		System.out.println(f.num); // 4
		System.out.println(z.num); // 5
		f.show(); // ZiShow
		z.show(); // ZiShow
	}
}
class Super{
	int i = 0;
	public Super(String s){ i=1; }
}
class test extends Super {
	public test(String s){i = 2;}
	public static void main(String[] args) {
		test d = new test("yes");
		System.out.println(d.i);
	}
}
// 编译失败:父类中没有空参构造函数,必须显示定义一个; 
class Super{
	public int get(){ return 4; }
}
class test extends Super {
	public long get(){ return 5; }
	public static void main(String[] args) {
		Super s = new test();
		System.out.println(s.get());
	}
}
// 编译失败:返回值类型不一样,覆盖错误;
interface Test { void fun(); }
class test {
	public static void main(String[] args) {
		new test().show(new Test() {
						   public void fun(){}
					    });
	}
	void show(Test t){ t.fun(); }
}
// 在主函数中补足代码:使用匿名内部类调用show方法;
// 主函数中不能直接调用show方法,因为show方法是非静态的;
// 所以需要先创建一个Demo类,通过类名调用show方法;
// 往show方法里面传值:接口型的引用;接口里面就一个方法,
// 这时候可以创建匿名内部类;传的值是接口类型的,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值