#include
using namespace std;
class A
{
int x;
public:
A() { cout << "A's constructor called " << endl; }
};
class B
{
static A a;
public:
B() { cout << "B's constructor called " << endl; }
static A getA() { return a; }
};
A B::a; // definition of a
int main()
{
B b1, b2, b3;
A a = b1.getA();
return 0;
}
背景:
閱讀新聞
Java編程思想重點(diǎn)筆記(Java開(kāi)發(fā)必看)
[日期:2015-01-09]
來(lái)源:Linux社區(qū)
作者:lanxuezaipiao
[字體:大 中 小]
Java編程思想,Java學(xué)習(xí)必讀經(jīng)典,不管是初學(xué)者還是大牛都值得一讀,這里總結(jié)書(shū)中的重點(diǎn)知識(shí),這些知識(shí)不僅經(jīng)常出現(xiàn)在各大知名公司的筆試面試過(guò)程中,而且在大型項(xiàng)目開(kāi)發(fā)中也是常用的知識(shí),既有簡(jiǎn)單的概念理解題(比如is-a關(guān)系和has-a關(guān)系的區(qū)別),也有深入的涉及RTTI和JVM底層反編譯知識(shí)转锈。
1. Java中的多態(tài)性理解(注意與C++區(qū)分)
Java中除了static方法和final方法(private方法本質(zhì)上屬于final方法,因?yàn)椴荒鼙蛔宇愒L問(wèn))之外,其它所有的方法都是動(dòng)態(tài)綁定,這意味著通常情況下,我們不必判定是否應(yīng)該進(jìn)行動(dòng)態(tài)綁定—它會(huì)自動(dòng)發(fā)生秕岛。
final方法會(huì)使編譯器生成更有效的代碼,這也是為什么說(shuō)聲明為final方法能在一定程度上提高性能(效果不明顯)。
如果某個(gè)方法是靜態(tài)的,它的行為就不具有多態(tài)性:
class StaticSuper {
public static String staticGet() {
return "Base static九度快排系統(tǒng) https://www.190seo.comGet()";
}
public String dynamicGet() {
return "Base dynamicGet()";
}
}
class StaticSub extends StaticSuper {
public static String staticGet() {
return "Derived staticGet()";
}
public String dynamicGet() {
return "Derived dynamicGet()";
}
}
public class StaticPolymorphism {
public static void main(String[] args) {
StaticSuper sup = new StaticSub();
System.out.println(sup.staticGet());
System.out.println(sup.dynamicGet());
}
}
輸出:
Base staticGet()
Derived dynamicGet()
構(gòu)造函數(shù)并不具有多態(tài)性,它們實(shí)際上是static方法,只不過(guò)該static聲明是隱式的孵滞。因此,構(gòu)造函數(shù)不能夠被override诬滩。
在父類構(gòu)造函數(shù)內(nèi)部調(diào)用具有多態(tài)行為的函數(shù)將導(dǎo)致無(wú)法預(yù)測(cè)的結(jié)果,因?yàn)榇藭r(shí)子類對(duì)象還沒(méi)初始化,此時(shí)調(diào)用子類方法不會(huì)得到我們想要的結(jié)果。
class Glyph {
void draw() {
System.out.println("Glyph.draw()");
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph(). radius=" + radius);
}
void draw() {
System.out.println("RoundGlyph.draw(). radius=" + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
輸出:
Glyph() before draw()
RoundGlyph.draw(). radius=0
Glyph() after draw()
RoundGlyph.RoundGlyph(). radius=5
為什么會(huì)這樣輸出?這就要明確掌握J(rèn)ava中構(gòu)造函數(shù)的調(diào)用順序:
(1)在其他任何事物發(fā)生之前,將分配給對(duì)象的存儲(chǔ)空間初始化成二進(jìn)制0;
(2)調(diào)用基類構(gòu)造函數(shù)伦腐。從根開(kāi)始遞歸下去,因?yàn)槎鄳B(tài)性此時(shí)調(diào)用子類覆蓋后的draw()方法(要在調(diào)用RoundGlyph構(gòu)造函數(shù)之前調(diào)用),由于步驟1的緣故,我們此時(shí)會(huì)發(fā)現(xiàn)radius的值為0;
(3)按聲明順序調(diào)用成員的初始化方法;
(4)最后調(diào)用子類的構(gòu)造函數(shù)赢底。
只有非private方法才可以被覆蓋,但是還需要密切注意覆蓋private方法的現(xiàn)象,這時(shí)雖然編譯器不會(huì)報(bào)錯(cuò),但是也不會(huì)按照我們所期望的來(lái)執(zhí)行,即覆蓋private方法對(duì)子類來(lái)說(shuō)是一個(gè)新的方法而非重載方法。因此,在子類中,新方法名最好不要與基類的private方法采取同一名字(雖然沒(méi)關(guān)系,但容易誤解,以為能夠覆蓋基類的private方法)柏蘑。
Java類中屬性域的訪問(wèn)操作都由編譯器解析,因此不是多態(tài)的幸冻。父類和子類的同名屬性都會(huì)分配不同的存儲(chǔ)空間,如下:
// Direct field access is determined at compile time.
class Super {
public int field = 0;
public int getField() {
return field;
}
}
class Sub extends Super {
public int field = 1;
public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("sup.filed=" + sup.field +
", sup.getField()=" + sup.getField());
Sub sub = new Sub();
System.out.println("sub.filed=" + sub.field +
", sub.getField()=" + sub.getField() +
", sub.getSuperField()=" + sub.getSuperField());
}
}
輸出:
sup.filed=0, sup.getField()=1
sub.filed=1, sub.getField()=1, sub.getSuperField()=0
Sub子類實(shí)際上包含了兩個(gè)稱為field的域,然而在引用Sub中的field時(shí)所產(chǎn)生的默認(rèn)域并非Super版本的field域,因此為了得到Super.field,必須顯式地指明super.field。
2. is-a關(guān)系和is-like-a關(guān)系
is-a關(guān)系屬于純繼承,即只有在基類中已經(jīng)建立的方法才可以在子類中被覆蓋,如下圖所示:
基類和子類有著完全相同的接口,這樣向上轉(zhuǎn)型時(shí)永遠(yuǎn)不需要知道正在處理的對(duì)象的確切類型,這通過(guò)多態(tài)來(lái)實(shí)現(xiàn)辩越。
is-like-a關(guān)系:子類擴(kuò)展了基類接口嘁扼。它有著相同的基本接口,但是他還具有由額外方法實(shí)現(xiàn)的其他特性。
缺點(diǎn)就是子類中接口的擴(kuò)展部分不能被基類訪問(wèn),因此一旦向上轉(zhuǎn)型,就不能調(diào)用那些新方法黔攒。
3. 運(yùn)行時(shí)類型信息(RTTI + 反射)
概念
RTTI:運(yùn)行時(shí)類型信息使得你可以在程序運(yùn)行時(shí)發(fā)現(xiàn)和使用類型信息。
使用方式
Java是如何讓我們?cè)谶\(yùn)行時(shí)識(shí)別對(duì)象和類的信息的,主要有兩種方式(還有輔助的第三種方式,見(jiàn)下描述):
一種是“傳統(tǒng)的”RTTI,它假定我們?cè)诰幾g時(shí)已經(jīng)知道了所有的類型,比如
另一種是“反射”機(jī)制,它運(yùn)行我們?cè)谶\(yùn)行時(shí)發(fā)現(xiàn)和使用類的信息,即使用强缘。
其實(shí)還有第三種形式,就是關(guān)鍵字,它返回一個(gè)bool值,它保持了類型的概念,它指的是“你是這個(gè)類嗎?或者你是這個(gè)類的派生類嗎?”督惰。而如果用==或equals比較實(shí)際的Class對(duì)象,就沒(méi)有考慮繼承—它或者是這個(gè)確切的類型,或者不是。
工作原理
要理解RTTI在Java中的工作原理,首先必須知道類型信息在運(yùn)行時(shí)是如何表示的,這項(xiàng)工作是由稱為的特殊對(duì)象完成的,它包含了與類有關(guān)的信息旅掂。Java送Class對(duì)象來(lái)執(zhí)行其RTTI,使用類加載器的子系統(tǒng)實(shí)現(xiàn)赏胚。
無(wú)論何時(shí),只要你想在運(yùn)行時(shí)使用類型信息,就必須首先獲得對(duì)恰當(dāng)?shù)腃lass對(duì)象的引用,獲取方式有三種:
(1)如果你沒(méi)有持有該類型的對(duì)象,則就是實(shí)現(xiàn)此功能的便捷途,因?yàn)樗恍枰獙?duì)象信息;
(2)如果你已經(jīng)擁有了一個(gè)感興趣的類型的對(duì)象,那就可以通過(guò)調(diào)用方法來(lái)獲取Class引用了,它將返回表示該對(duì)象的實(shí)際類型的Class引用。Class包含很有有用的方法,比如:
package rtti;
interface HasBatteries{}
interface WaterProof{}
interface Shoots{}
class Toy {
Toy() {}
Toy(int i) {}
}
class FancyToy extends Toy
implements HasBatteries, WaterProof, Shoots {
FancyToy() {
super(1);
}
}
public class RTTITest {
static void printInfo(Class cc) {
System.out.println("Class name: " + cc.getName() +
", is interface? [" + cc.isInterface() + "]");
System.out.println("Simple name: " + cc.getSimpleName());
System.out.println("Canonical name: " + cc.getCanonicalName());
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("rtti.FancyToy"); // 必須是全限定名(包名+類名)
} catch(ClassNotFoundException e) {
System.out.println("Can't find FancyToy");
System.exit(1);
}
printInfo(c);
for(Class face : c.getInterfaces()) {
printInfo(face);
}
Class up = c.getSuperclass();
Object obj = null;
try {
// Requires default constructor.
obj = up.newInstance();
} catch (InstantiationException e) {
System.out.println("Can't Instantiate");
System.exit(1);
} catch (IllegalAccessException e) {
System.out.println("Can't access");
System.exit(1);
}
printInfo(obj.getClass());
}
}
輸出:
Class name: rtti.FancyToy, is interface? [false]
Simple name: FancyToy
Canonical name: rtti.FancyToy
Class name: rtti.HasBatteries, is interface? [true]
Simple name: HasBatteries
Canonical name: rtti.HasBatteries
Class name: rtti.WaterProof, is interface? [true]
Simple name: WaterProof
Canonical name: rtti.WaterProof
Class name: rtti.Shoots, is interface? [true]
Simple name: Shoots
Canonical name: rtti.Shoots
Class name: rtti.Toy, is interface? [false]
Simple name: Toy
Canonical name: rtti.Toy
(3)Java還提供了另一種方法來(lái)生成對(duì)Class對(duì)象的引用,即使用類字面常量商虐。比如上面的就像這樣:來(lái)引用觉阅。
這樣做不僅更簡(jiǎn)單,而且更安全,因?yàn)樗诰幾g時(shí)就會(huì)受到檢查(因此不需要置于try語(yǔ)句塊中),并且它根除了對(duì)forName方法的引用,所以也更高效。類字面常量不僅可以應(yīng)用于普通的類,也可以應(yīng)用于接口秘车、數(shù)組以及基本數(shù)據(jù)類型典勇。
注意:當(dāng)使用“.class”來(lái)創(chuàng)建對(duì)Class對(duì)象的引用時(shí),不會(huì)自動(dòng)地初始化該Class對(duì)象,初始化被延遲到了對(duì)靜態(tài)方法(構(gòu)造器隱式的是靜態(tài)的)或者非final靜態(tài)域(注意final靜態(tài)域不會(huì)觸發(fā)初始化操作)進(jìn)行首次引用時(shí)才執(zhí)行:。而使用Class.forName時(shí)會(huì)自動(dòng)的初始化叮趴。
為了使用類而做的準(zhǔn)備工作實(shí)際包含三個(gè)步驟:
- 加載:由類加載器執(zhí)行割笙。查找字節(jié)碼,并從這些字節(jié)碼中創(chuàng)建一個(gè)Class對(duì)象
- 鏈接:驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間,并且如果必需的話,將解析這個(gè)類創(chuàng)建的對(duì)其他類的所有引用。
- 初始化:如果該類具有超類,則對(duì)其初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊眯亦。
這一點(diǎn)非常重要,下面通過(guò)一個(gè)實(shí)例來(lái)說(shuō)明這兩者的區(qū)別:
package rtti;
import java.util.Random;
class Initable {
static final int staticFinal = 47;
static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
class Initable3 {
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) {
// Does not trigger initialization
Class initable = Initable.class;
System.out.println("After creating Initable ref");
// Does not trigger initialization
System.out.println(Initable.staticFinal);
// Does trigger initialization(rand() is static method)
System.out.println(Initable.staticFinal2);
// Does trigger initialization(not final)
System.out.println(Initable2.staticNonFinal);
try {
Class initable3 = Class.forName("rtti.Initable3");
} catch (ClassNotFoundException e) {
System.out.println("Can't find Initable3");
System.exit(1);
}
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
輸出:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
RTTI的限制?如何突破? — 反射機(jī)制
如果不知道某個(gè)對(duì)象的確切類型,RTTI可以告訴你,但是有一個(gè)限制:這個(gè)類型在編譯時(shí)必須已知,這樣才能使用RTTI識(shí)別它,也就是在編譯時(shí),編譯器必須知道所有要通過(guò)RTTI來(lái)處理的類伤溉。
可以突破這個(gè)限制嗎?是的,突破它的就是反射機(jī)制般码。
類與類庫(kù)一起對(duì)反射的概念進(jìn)行了支持,該類庫(kù)包含了、以及類(每個(gè)類都實(shí)現(xiàn)了接口)乱顾。這些類型的對(duì)象是由JVM在運(yùn)行時(shí)創(chuàng)建的,用以表示未知類里對(duì)應(yīng)的成員板祝。這樣你就可以使用創(chuàng)建新的對(duì)象,用方法讀取和修改與對(duì)象關(guān)聯(lián)的字段,用方法調(diào)用與對(duì)象關(guān)聯(lián)的方法。另外,還可以調(diào)用等很便利的方法,以返回表示字段走净、方法以及構(gòu)造器的對(duì)象的數(shù)組扔字。這樣,匿名對(duì)象的類信息就能在運(yùn)行時(shí)被完全確定下來(lái),而在編譯時(shí)不需要知道任何事情。
更多詳情見(jiàn)請(qǐng)繼續(xù)閱讀下一頁(yè)的精彩內(nèi)容: http://www.linuxidc.com/Linux/2015-01/111506p2.htm
12下一頁(yè)
10道C++輸出易錯(cuò)筆試題收集
優(yōu)酷土豆2014校園招聘筆試題目之Java開(kāi)發(fā)類
相關(guān)資訊
Java編程
Java 老矣,尚能飯否? (今 19:38)
Java 并發(fā)編程中使用 ReentrantLoc (12/05/2015 22:49:19)
for(int a:i)在Java 編程中的使用 (08/24/2015 07:29:34)
Java編程基本概念 (03月07日)
10個(gè)實(shí)用的但偏執(zhí)的Java編程技術(shù) (08/31/2015 17:39:22)
Java:過(guò)去温技、未來(lái)的互聯(lián)網(wǎng)編程之王 (07/31/2015 14:50:02)
本文評(píng)論
查看全部評(píng)論 (1)
表情:
姓名:
匿名
字?jǐn)?shù)
同意評(píng)論聲明
評(píng)論聲明
尊重網(wǎng)上道德,遵守中華人民共和國(guó)的各項(xiàng)有關(guān)法律法規(guī)
承擔(dān)一切因您的行為而直接或間接導(dǎo)致的民事或刑事法律責(zé)任
本站管理人員有權(quán)保留或刪除其管轄留言中的任意內(nèi)容
本站有權(quán)在網(wǎng)站內(nèi)轉(zhuǎn)載或引用您的評(píng)論
參與本評(píng)論即表明您已經(jīng)閱讀并接受上述條款
第 1 樓 pcfine 發(fā)表于 2015/1/12 13:10:44mark回復(fù) 支持 (0) 反對(duì) (0)
最新資訊
Java 老矣,尚能飯否?
Google 再次從官方商店下架偽裝成合法程序
一銘桌面操作系統(tǒng)4.0 SP1安裝使用初體驗(yàn)
Opus 1.2發(fā)布,開(kāi)源免專利費(fèi)音頻編解碼器
Java中抽象類的定義和使用
Java實(shí)現(xiàn)內(nèi)部類
C語(yǔ)言如何分離一個(gè)數(shù)的高低位,如何將兩個(gè)
C語(yǔ)言之鞍點(diǎn)的查找
C語(yǔ)言實(shí)現(xiàn)牛頓迭代法解方程
Android編譯系統(tǒng)產(chǎn)品線(基于友善之臂