Java中主要有如下幾種類型的變量- 局部變量
- 類變量(靜態變量)-- 屬於類
- 成員變量(非靜態變量)-- 屬於對象
2、關於枚舉
package com.scu.lly;
public class EnumTest {
/**
* 顏色枚舉
*/
enum ColorEnum{
RED,
GREEN,
BLUE
}
/**
* 性別枚舉
* 可用中文字符,不能單獨使用數字
* (枚舉值組成:字母、下劃線)
*/
enum SexEnum{
男,
女,
MAN,
WOWAM
}
/**
* 1、帶有構造方法的枚舉,構造方法爲只能爲private(默認可不寫private);
* 2、含帶參構造方法的枚舉,枚舉值必須賦值;
* 3、枚舉中有了其他屬性或方法之後,枚舉值必須定義在最前面,且需要在最後一個枚舉值後面加分號";"
*/
enum CarEnum{
BMW("寶馬",1000000),
JEEP("吉普",800000),
MINI("mini",200000);
private String name;
/**
* 從這裏可以看出雖然枚舉值不能直接由數字組成,但是我們可以給該枚舉類添加一個int類型的值,通過構造方法給其賦值,相當於間接的可以使用數值
*/
private int price;
private CarEnum(String name,int price){
this.name = name;
this.price = price;
}
//添加setter、getter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
/**
* 由於枚舉類都繼承了Enum類,故我們定義的enum都不能在繼承其他類了,但是可以實現其他接口
*/
enum CarSetEnum implements Car{
BMW("寶馬"),
JEEP("吉普"),
MINI("mini");
private String name;
private CarSetEnum(String name){
this.name = name;
}
//添加setter、getter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void setCarName(String name) {
this.name = name;
}
}
public static void main(String[] args){
ColorEnum color = ColorEnum.BLUE;
switch(color){
case RED:
System.out.println("紅色");
break;
case GREEN:
System.out.println("綠色");
break;
case BLUE:
System.out.println("藍色");
break;
}
getSelectedColor(color);
//測試含構造方法的枚舉
System.out.println("吉普信息:"+CarEnum.JEEP.getName() + ":" +CarEnum.JEEP.price);
for(CarEnum car : CarEnum.values()){
System.out.println(car.name);
System.out.println(car.getPrice());
}
//測試實現接口的枚舉
CarSetEnum.BMW.setName("加長寶馬");
System.out.println(CarSetEnum.BMW.getName());
}
public static ColorEnum getSelectedColor(ColorEnum color){
ColorEnum result;
switch(color){
case RED:
System.out.println("紅色");
break;
case GREEN:
System.out.println("綠色");
break;
case BLUE:
System.out.println("藍色");
break;
}
result = color;
return result;
}
interface Car{
public void setCarName(String name);
}
}
3、訪問控制修飾符
修飾符 |
說明 |
---|
private
|
私有的,在同一類內可見。
|
默認沒寫 |
在同一包(包括子類和非子類)內可見。默認不使用任何修飾符。 |
protected |
受保護的,對同一包內的類和所有子類可見。 |
public |
共有的,對所有類可見。 |
主要是默認和protected這兩個修飾符,總結起來就是:
默認的:同一包下可訪問;
protected:同一包和所有子類可訪問;
(1)這裏的可見、可訪問指的是能不能通過 ”類的對象.變量名“的方式訪問,這是因爲除static聲明的變量屬於類變量外,其他的都屬於實例變量,是屬於某個對象的!
如,Person p = new Person(); p.age直接訪問age變量,對於那些私有的變量,很多情況下會對外提供public的setter和getter方法來供外部訪問。
(2)要注意的是,對於有繼承關係的子類來說,比如 class A extends B,A直接繼承擁有了默認的(在同一包下)、protected、public的這個字段,可以直接使用該字段,而不用通過再次的實例化父類或"父類對象.字段"的形式訪問,因爲在實例化A類的時候父類B已經實例化好了。特別的,對於protected來說,如下形式是編譯不能通過的。
package com.a
public class A extends B{
public void test(){
B b = new B();
String str = b.age;//錯誤!不同包下的子類不能通過實例出來的父類獲取protected的變量
String str2 = age;//正確,A類繼承了B,直接擁有了該字段
String str3 = b.birthday;//正確,birthday爲public
}
}
package com.b
public class B{
protected String age = "20";
public String birthday = "1995";
}
結論就是:
在上面那個表格修飾符的約束前提下,對於非繼承類關係,需要使用 “實例變量.變量名的形式訪問”(static類變量除外);
對於有繼承關係的子類來說, 子類直接繼承了擁有了默認的(在同一包下)、protected、public的這個字段,可以直接使用該字段。
4、UTF-8和GBK編碼轉換
下面哪段程序能夠正確的實現了GBK編碼字節流到UTF-8編碼字節流的轉換:byte
[]
src,dst;
A、dst=String.fromBytes(src,"GBK").getBytes("UTF-8")
B、dst=new String(src,"GBK").getBytes("UTF-8")
C、dst=new String("GBK",src).getBytes()
D、dst=String.encode(String.decode(src,"GBK")),"UTF-8" )
正確答案:B
操作步驟就是先解碼再編碼,先通過GBK編碼還原字符串,在該字符串正確的基礎上得到“UTF-8”所對應的字節串。
5、try、catch、finally執行順序問題
下面函數將返回?
1
2
3
4
5
6
7
8
9
|
public static int func
(){
try {
return 1;
} catch (Exception
e){
return 2;
}finally{
return 3;
}
}
|
A、1 B、2 C、3 D、編譯錯誤
正確答案:C
(1)try catch中只要有finally語句都要執行(有特例:如果try 或 catch 裏面有 exit(0)就不會執行finally了);
(2)finally語句在try或catch中的return語句執行之後返回之前執行,且finally裏的修改語句不能影響try或catch中
return已經確定的返回值;
若finally裏也有return語句則覆蓋try或catch中的return語句直接返回;
(3)在遵守第(2)條return的情況下,執行順序是:try-->catch(如果有異常的話)-->finally;
6、靜態代碼塊、子類、父類初始化順序
如下代碼的輸出結果:
package com.scu.lly;
public class HelloB extends HelloA {
public HelloB() {
System.out.println("-----------HelloB 構造方法------------");
}
{
System.out.println("I’m B class");
}
static{
System.out.println("static B");
}
public static void main(String[] args){
new HelloB();
}
}
class HelloA{
public HelloA(){
System.out.println("-----------HelloA 構造方法------------");
}
{
System.out.println("I’m A class");
}
static{
System.out.println("static A");
}
}
輸出結果:
static A
static B
I’m A class
-----------HelloA 構造方法------------
I’m B class
-----------HelloB 構造方法------------
執行順序:1.靜態代碼塊 --> 2.普通代碼塊 --> 3.構造方法
需要明白的是,1是類級別的,2和3是實例級別的,所以在父子類關係中,上述的執行順序爲:
父類靜態代碼塊-->子類靜態代碼塊-->父類普通代碼塊-->父類構造方法-->子類代碼塊-->子類構造方法;
也就是上到下(父類到子類)先走完 類級別的(靜態的)--> 再依次走完父類的所有實例級別代碼
--> 再走子類所有實例級別代碼
7、關於null對象、static變量和方法
有關下述Java代碼描述正確的選項是____。
1
2
3
4
5
6
7
8
|
public class TestClass
{
private static void testMethod(){
System.out.println( "testMethod" );
}
public static void main(String[]
args) {
((TestClass) null ).testMethod();
}
}
|
A、編譯不通過 B、編譯通過,運行異常,報NullPointerException C、編譯通過,運行異常,報IllegalArgumentException D、編譯通過,運行異常,報NoSuchMethodException E、編譯通過,運行異常,報Exception F、運行正常,輸出testMethod
正確答案:F
靜態方法是屬於類的,靜態方法在對象實例創建前就已經存在了,它的使用不依賴於對象是否被創建。當我們通過類的實例來調用時,最後實際上還是將對象實例轉換成了類去掉用該靜態方法,所以這裏的null只是迷惑大家的跟它沒有什麼關係。
這裏
((TestClass)
null
).testMethod();
也可以寫成TestClass
t = null; t.testMethod();同樣可以正確輸出。null可以被強制轉換成任意類型對象,雖然這個時候t被賦爲了空,但這個“空對象”也是屬於TestClass的,那麼這個“空對象”也就可以去堆上的靜態方法區調用testMethod()方法了。
如果這裏testMethod把static去掉,該testMethod方法就變成了實例對象的方法了。這時,可以編譯通過,但是會報空指針。
同理,對於static變量也是一樣的。比如TestClass 中有如下變量:private static String str = "abc"; 我們通過TestClass
t = null; System.out.println(t.str);同樣可以正確輸出。
8、關於線程啓動
下列方法中哪個是執行線程的方法? ()
A、run() B、start() C、sleep() D、suspend()
正確答案:A
start()方法啓動一個線程,使其處於就緒狀態,得到了CPU就會執行,而調用run()方法,就相當於是普通的方法調用,會在主線程中直接運行,此時沒有開啓一個線程。如下:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----------線程中run---------");
}
});
System.out.println("-------主線程11111------");
t.run();
System.out.println("-------主線程222------");
這裏調用Thread的run()方法,此時相當於是普通的方法調用,並沒有開啓線程,直接在主線程中運行Thread中的Runnable方法,睡眠2秒後打印"----------線程中run---------",在執行t.run後面的打印。所以此時的輸出爲:
-------主線程11111------
----------線程中run---------
-------主線程222------
若將t.run()改爲t.start(),此時會開啓一個線程,並使該線程處於就緒狀態,得到CPU後開始執行,調用t.start()後主線程繼續執行下面的打印,所以此時的輸出爲:
-------主線程11111------
-------主線程222------
----------線程中run---------
9、關於內部類
往OuterClass類的代碼段中插入內部類聲明, 哪一個是錯誤的:
1
2
3
4
|
public class OuterClass{
private float f=1.0f;
//插入代碼到這裏
}
|
A、class InnerClass{
public static float func(){return f;}
}
B、abstract class InnerClass{
public abstract float func(){}
}
C、static class InnerClass{
protected static float func(){return f;}
}
D、public class InnerClass{
static float func(){return f;}
}
正確答案:ABCD
靜態的內部類纔可以定義static方法,排除AD;
B抽象方法中不能有方法體;
C,靜態方法不能夠引用非靜態變量。
10、Final修飾符、volatile修飾符
Final修飾符,用來修飾類、方法和變量,final修飾的類不能夠被繼承,修飾的方法可以被繼承,重載,但是不能被子類重寫(即重新定義)
(1)final變量:
被聲明爲final的對象的引用不能指向不同的對象。但是final對象裏的數據可以被改變。也就是說final對象的引用不能改變,但是裏面的值可以改變。比如:
final Person p = new Person();
p.name = "aaa";
p.name = "bbb";
但是,如果是final String str = "aaa"; str = "bbb";//錯誤編譯不能通過,因爲此時str的引用已經改變了!
(2)final修飾方法
Final修飾的方法可以被子類繼承,但是不能被子類修改(重寫)。
聲明final方法的主要目的是防止該方法的內容被修改。
volatile修飾符,Volatile修飾的成員變量在每次被線程訪問時,都強迫從共享內存中重讀該成員變量的值。而且,當成員變量發生變化時,強迫線程將變化值回寫到共享內存。這樣在任何時刻,兩個不同的線程總是看到某個成員變量的同一個值。一個volatile對象引用可能是null。
11、StringBuffer
和 StringBuilder
和String類不同,StringBuffer和StringBuilder類的對象能夠被多次的修改,並且不產生新的未使用對象。
StringBuilder類和StringBuffer之間的最大不同在於StringBuilder的方法不是線程安全的(不能同步訪問)。
由於StringBuilder相較於StringBuffer有速度優勢,所以多數情況下建議使用StringBuilder類。然而在應用程序要求線程安全的情況下,則必須使用StringBuffer類。
12、可變參數
JDK 1.5 開始,Java支持傳遞同類型的可變參數給一個方法,一個方法中只能指定一個可變參數,它必須是方法的最後一個參數。任何普通的參數必須在它之前聲明。
如,public void getStr(String ...str){}//正確
public void getStr2(String ...str,int a){}//錯誤,一個方法中可變參數只能有一個,它必須是方法的最後一個參數。
13、關於異常分類
所有的異常類是從Java.lang.Exception類繼承的子類。
Exception類是Throwable類的子類。除了Exception類外,Throwable還有一個子類Error 。Error用來指示運行時環境發生的錯誤。層次關係如圖:
檢查性異常: 不處理編譯不能通過
非檢查性異常:不處理編譯可以通過,如果有拋出直接拋到控制檯。
運行時異常(RuntimeException): 繼承自RuntimeException類的就是非檢查性異常
非運行時異常: 就是檢查性異常
下面的表中列出了Java的非檢查性異常(RuntimeException)。
異常 |
描述 |
---|
ArithmeticException |
當出現異常的運算條件時,拋出此異常。例如,一個整數"除以零"時,拋出此類的一個實例。 |
ArrayIndexOutOfBoundsException |
用非法索引訪問數組時拋出的異常。如果索引爲負或大於等於數組大小,則該索引爲非法索引。 |
ArrayStoreException |
試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常。 |
ClassCastException |
當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。 |
IllegalArgumentException |
拋出的異常表明向方法傳遞了一個不合法或不正確的參數。 |
IllegalMonitorStateException |
拋出的異常表明某一線程已經試圖等待對象的監視器,或者試圖通知其他正在等待對象的監視器而本身沒有指定監視器的線程。 |
IllegalStateException |
在非法或不適當的時間調用方法時產生的信號。換句話說,即 Java 環境或 Java 應用程序沒有處於請求操作所要求的適當狀態下。 |
IllegalThreadStateException |
線程沒有處於請求操作所要求的適當狀態時拋出的異常。 |
IndexOutOfBoundsException |
指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。 |
NegativeArraySizeException |
如果應用程序試圖創建大小爲負的數組,則拋出該異常。 |
NullPointerException |
當應用程序試圖在需要對象的地方使用 null 時,拋出該異常 |
NumberFormatException |
當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換爲適當格式時,拋出該異常。 |
SecurityException |
由安全管理器拋出的異常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException |
此異常由 String 方法拋出,指示索引或者爲負,或者超出字符串的大小。 |
UnsupportedOperationException |
當不支持請求的操作時,拋出該異常。 |
下面的表中列出了Java定義在java.lang包中的檢查性異常類。
異常 |
描述 |
---|
ClassNotFoundException |
應用程序試圖加載類時,找不到相應的類,拋出該異常。 |
CloneNotSupportedException |
當調用 Object 類中的 clone 方法克隆對象,但該對象的類無法實現 Cloneable 接口時,拋出該異常。 |
IllegalAccessException |
拒絕訪問一個類的時候,拋出該異常。 |
InstantiationException |
當試圖使用 Class 類中的 newInstance 方法創建一個類的實例,而指定的類對象因爲是一個接口或是一個抽象類而無法實例化時,拋出該異常。 |
InterruptedException |
一個線程被另一個線程中斷,拋出該異常。 |
NoSuchFieldException |
請求的變量不存在 |
NoSuchMethodException |
請求的方法不存在 |
其他
1、下面描述屬於java虛擬機功能的是?
A、通過 ClassLoader 尋找和裝載 class 文件
B、解釋字節碼成爲指令並執行,提供 class 文件的運行環境
正確答案:ABCD
2、下面有關java threadlocal說法正確的有?
A、ThreadLocal存放的值是線程封閉,線程間互斥的,主要用於線程內共享一些數據,避免通過參數來傳遞
B、線程的角度看,每個線程都保持一個對其線程局部變量副本的隱式引用,只要線程是活動的並且 ThreadLocal 實例是可訪問的;在線程消失之後,其線程局部實例的所有副本都會被垃圾回收
C、在Thread類中有一個Map,用於存儲每一個線程的變量的副本。
D、對於多線程資源共享的問題,同步機制採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式
正確答案:ABCD
ThreadLocal類用於創建一個線程本地變量
在Thread中有一個成員變量ThreadLocals,該變量的類型是ThreadLocalMap,也就是一個Map,它的鍵是threadLocal,值就是變量的副本,ThreadLocal爲每一個使用該變量的線程都提供了一個變量值的副本,每一個線程都可以獨立地改變自己的副本,是線程隔離的。通過ThreadLocal的get()方法可以獲取該線程變量的本地副本,在get方法之前要先set,否則就要重寫initialValue()方法。
ThreadLocal不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。
3、以下集合對象中哪幾個是線程安全的?( )
A、ArrayList
B、Vector
C、Hashtable
D、Stack
正確答案:BCD
在集合框架中,有些類是線程安全的,這些都是jdk1.1中的出現的。在jdk1.2之後,就出現許許多多非線程安全的類。 下面是這些線程安全的同步的類:
vector:比arraylist多了個同步化機制(線程安全),因爲效率較低,現在已經不太建議使用。在web應用中,特別是前臺頁面,往往效率(頁面響應速度)是優先考慮的。
statck:堆棧類,繼承了Vector
hashtable:比hashmap多了個線程安全
enumeration:枚舉,相當於迭代器
除了這些之外,其他的都是非線程安全的類和接口。比如常用的ArrayList、HashMap都是線程不安全的。