文章目錄
- 一個".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方法裏面傳值:接口型的引用;接口裏面就一個方法,
// 這時候可以創建匿名內部類;傳的值是接口類型的,