文章目录
- 一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?
- 在 JAVA 中如何跳出当前的多重嵌套循环
- switch 语句能否作用在 byte/long/String 上?
- `short s1 = 1; s1 = s1 + 1;`对错? `short s1 = 1; s1 += 1;`对错?
- char 型变量中能不能存贮一个中文汉字?为什么?
- 用最有效率的方法算出 2 乘以 8 等于几?
- Integer 与 int 的区别
- `super.getClass().getName()`输出结果
- `String s = "Hello";s = s + " world!";`这两行代码执行后,原始的 String 对象中的内容到底变了没有?
- `String s = new String("xyz");`创建了几个 String Object?二者之间有什么区别?
- 数组没有length()这个方法,有length 的属性;String 有 length()方法
- 下面这条语句一共创建了多少个对象:`String s="a"+"b"+"c"+"d";`
- try {}里有一个 return 语句,那么紧跟在这个 try 后的 finally {}里的 code会不会被执行?什么时候被执行?在 return 前还是后?
- Thread中有run,传入的d 中也有run,为什么调用的是d.run?
- 当一个线程进入一个对象的一个 synchronized 方法后,其它线程是否可进入此对象的其它方法?
- Collection 框架中实现比较要实现什么接口
- 两个对象值相同(x.equals(y) == true),但却可有不同的 hash code,这句话对不对?
- TreeSet 里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的 compareTo 方法,还是使用的子类的 compareTo 方法,还是抛异常
- 说出一些常用的类,包,接口,请各举 5 个
- 代码查错
- 创建两个线程交替执行
- 自动类型提升与强制类型转换
- 对两个整数变量的值进行互换 int a=3,b=5;
- switch~case
- **~ 面向对象练习题**
一个".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.lang
、 java.io
、 java.util
、java.sql
、javax.servlet
、org.hibernate
常用的接口: List
、Map
、Servlet
、HttpServletRequest
、HttpServletResponse
、Session(Hibernate)
、HttpSession
;
常用的类:BufferedReader
、BufferedWriter
、FileReader
、FileWirter
、String
、Integer
java.util.Date
、System
、Class
;
代码查错
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方法里面传值:接口型的引用;接口里面就一个方法,
// 这时候可以创建匿名内部类;传的值是接口类型的,