導航:首頁 > 編程語言 > syntheticjava

syntheticjava

發布時間:2022-05-04 10:18:58

A. java中Class<>和Class()有什麼不同

Class<T>是java類型,即只要是Object類的子類都可以

class () 是確定的類型,即 class a(),則類型就為a

B. 什麼是Java復合類

包含匿名內部類的,應該算是一種Synthetic的類

C. java文件是怎麼執行的

首先:使用javac命令把.java文件編譯成.class文件(位元組碼文件)
然後:JVM(java虛擬機)裝載.class文件並翻譯成機器碼後運行java程序;
共同學習-----請參考:
我們手工執行java程序是這樣的:
1 在記事本中或者是UE的文本編輯器中,寫好源程序;
2 使用javac命令把源程序編譯成.class文件;
編譯後的.class(類位元組碼)文件中會包含以下內容:

ConstantPool:符號表;
FieldInfo:類中的成員變數信息;
MethodInfo:類中的方法描述;
Attribute:可選的附加節點。
FieldInfo節點包含成員變數的名稱,諸如public,private,static等的標志。ConstantValue屬性用來存儲靜態的不變的成員變數的值。Deprecated和Synthetic被用來標記一個成員變數是不被推薦的或由編譯器生成的。

3 有了.class文件,我們執行 java 解釋命令就可以運行java程序了。

現在我們主要討論一下,當執行 java這個命令後,會發生什麼事情呢?
首先,JVM裝載.class,也就是類裝載器裝載類位元組碼。一個類裝載器本身也是一個java類,所以,類裝載器自身也需要被另外一個類裝載器裝載,這就出現了類似先有蛋,還是先有雞的問題。但JAVA中的類裝載器的這個問題卻很容易解決。JAVA的虛擬機(JVM)中內嵌了一個稱為Bootstrap類裝載器,它是用特定於操作系統的本地代碼實現的,屬於JAVA虛擬機的內核,Bootstrap類不用專門的類裝載器去進行裝載。Bootstrap類負責載入JAVA核心包中的類(即rt.jar文件中的類),這些類的Class.getClassLoader()方法返回值為null,即表示是Bootstrap類裝載器。JAVA核心包中有另外兩個類裝載器:ExtClassLoader和AppClassLoader,它們都是用JAVA語言編寫的JAVA類,其中ExtClassLoader類裝載負責載入存放在<JAVA_HOME>/jre/lib/ext目錄下的jar包中的類,AppClassLoader負責載入應用程序的啟動執行類,即當使用java命令去啟動執行一個類時,JAVA虛擬機使用AppClassLoader載入這個類。在編譯和運行JAVA程序時,都會通過ExtClassLoader類裝載器去<JAVA_HOME>/jre/lib/ext目錄下的JAR包中搜索要載入的類,所以,如果將包含例如Servlet API的jar包或者是javamail.jar包復制到該目錄下,在編譯Servlet或JavaMail程序時,就不必在CLASSPATH環境變數中增加包含Servlet API的jar包或者是javamail.jar包文件。
以上,就是一個JAVA程序執行的大致過程。

D. Java 枚舉型為什麼是靜態的,以及是怎麼實現的

是的,Java枚舉型是靜態常量,隱式地用static final修飾過。

確切地說,Java枚舉型是「靜態常量」,這裡麵包含了兩層意思:

枚舉型中的實例隱式地用static final修飾過。
枚舉型作為某個類中的成員欄位也隱式地用static final修飾過。

public class Traffic{
public enum Light{GREEN,YELLOW,RED}
}

還是你上面這個代碼,反編譯一下,你就能看到--編譯器背著你偷偷做了哪些手腳:
/*
* Decompiled with CFR 0_118.
*/
package com.ciaoshen.thinkinjava.chapter19;

public class Traffic {

public static final class Light
extends Enum<Light> {
public static final /* enum */ Light GREEN = new Light();
public static final /* enum */ Light YELLOW = new Light();
public static final /* enum */ Light RED = new Light();
private static final /* synthetic */ Light[] $VALUES;

public static Light[] values() {
return (Light[])$VALUES.clone();
}

public static Light valueOf(String string) {
return Enum.valueOf(Light.class, string);
}

private Light() {
super(string, n);
}

static {
$VALUES = new Light[]{GREEN, YELLOW, RED};
}
}

}

首先,枚舉型Light是個實實在在的類。繼承自基類Enum<Light>。然後在你不知情的情況下,偷偷加了static final修飾詞。

然後三個枚舉實例GREEN, YELLOW, RED也確確實實是Light的實例。然而前面也加上了static final。

然後構造器也被偷偷地閹割成private。這種實例控制手段,是不是在單例器模式里也見過?所以枚舉也是實現單例器的一種方法。

然後編譯器還偷偷合成了Light[]數組,一個values( )方法,一個valueOf( )方法。這個values( )在Enum的文檔里都找不到。

如果在Enum里定義一個相關方法,你還會看到一個匿名內部類:
public enum Light{
GREEN{public void show(){System.out.println("Green");}},
YELLOW{public void show(){System.out.println("Yellow");}},
RED{public void show(){System.out.println("Red");}};
}

反編譯的結果如下:
/*
* Decompiled with CFR 0_118.
*/
package com.ciaoshen.thinkinjava.chapter18;

import java.io.PrintStream;

public class Light
extends Enum<Light> {
public static final /* enum */ Light GREEN = new Light("GREEN", 0){

public void show() {
System.out.println("Green");
}
};
public static final /* enum */ Light YELLOW = new Light("YELLOW", 1){

public void show() {
System.out.println("Yellow");
}
};
public static final /* enum */ Light RED = new Light("RED", 2){

public void show() {
System.out.println("Red");
}
};
private static final /* synthetic */ Light[] $VALUES;

public static Light[] values() {
return (Light[])$VALUES.clone();
}

public static Light valueOf(String string) {
return Enum.valueOf(Light.class, string);
}

private Light() {
super(string, n);
}

static {
$VALUES = new Light[]{GREEN, YELLOW, RED};
}

}

總之,Java的Enum枚舉型整個就是一個大大的「語法糖」。明明是一個完整的類,但只向用戶暴露幾個靜態常量,隱藏掉大部分實現的細節。

E. 有java混淆器的eclipse插件嗎

android應用程序的混淆打包

1 . 在工程文件project.properties中加入下proguard.config=proguard.cfg , 如下所示:

target=android-8

proguard.config=proguard.cfg

Eclipse會通過此配置在工程目錄生成proguard.cfg文件

2 . 生成keystore (如已有可直接利用)

按照下面的命令行 在D:\Program Files\Java\jdk1.6.0_07\bin>目錄下,輸入keytool -genkey -alias android.keystore -keyalg RSA -validity 100000 -keystore android.keystore

參數意義:-validity主要是證書的有效期,寫100000天;空格,退格鍵 都算密碼。

命令執行後會在D:\Program Files\Java\jdk1.6.0_07\bin>目錄下生成 android.keystore文件。

3. 在Eclipce的操作

File -> Export -> Export Android Application -> Select project -> Using the existing keystore , and input password -> select the destination APK file

經過混淆後的源代碼,原先的類名和方法名會被類似a,b,c。。。的字元所替換,混淆的原理其實也就是類名和方法名的映射。

proguard 自己考一個就行

----------------------------------------------------------------------------------------

proguard 原理

Java代碼編譯成二進制class 文件,這個class
文件也可以反編譯成源代碼 ,除了注釋外,原來的code 基本都可以看到。為了防止重要code
被泄露,我們往往需要混淆(Obfuscation code , 也就是把方法,欄位,包和類這些java
元素的名稱改成無意義的名稱,這樣代碼結構沒有變化,還可以運行,但是想弄懂代碼的架構卻很難。 proguard
就是這樣的混淆工具,它可以分析一組class 的結構,根據用戶的配置,然後把這些class 文件的可以混淆java
元素名混淆掉。在分析class 的同時,他還有其他兩個功能,刪除無效代碼(Shrinking 收縮),和代碼進行優化
(Optimization Options)。

預設情況下,proguard
會混淆所有代碼,但是下面幾種情況是不能改變java 元素的名稱,否則就會這樣就會導致程序出錯。

一, 我們用到反射的地方。

二,
我們代碼依賴於系統的介面,比如被系統代碼調用的回調方法,這種情況最復雜。

三, 是我們的java
元素名稱是在配置文件中配置好的。

所以使用proguard時,我們需要有個配置文件告訴proguard
那些java 元素是不能混淆的。

proguard 配置

最常用的配置選項

-dontwarn 預設proguard
會檢查每一個引用是否正確,但是第三方庫裡面往往有些不會用到的類,沒有正確引用。如果不配置的話,系統就會報錯。

-keep 指定的類和類成員被保留作為 入口


-keepclassmembers
指定的類成員被保留。

-keepclasseswithmembers
指定的類和類成員被保留,假如指定的類成員存在的話。

proguard 問題和風險

代碼混淆後雖然有混淆優化的好處,但是它往往也會帶來如下的幾點問題

1,混淆錯誤,用到第三方庫的時候,必須告訴 proguard
不要檢查,否則proguard 會報錯。

2,運行錯誤,當code
不能混淆的時候,我們必須要正確配置,否則程序會運行出錯,這種情況問題最多。

3,調試苦難,出錯了,錯誤堆棧是混淆後的代碼
,自己也看不懂。

為了防止混淆出問題,你需要熟悉你所有的code ,系統的架構
,以及系統和你code的集成的介面,並細心分析。 同時你必須需要一輪全面的測試。 所以混淆也還是有一定風險的。
為了避免風險,你可以只是混淆部分關鍵的代碼,但是這樣你的混淆的效果也會有所降低。

常見的不能混淆的androidCode

Android 程序
,下面這樣代碼混淆的時候要注意保留。

Android系統組件,系統組件有固定的方法被系統調用。

被Android Resource
文件引用到的。名字已經固定,也不能混淆,比如自定義的View 。

Android Parcelable ,需要使用android
序列化的。

其他Anroid 官方建議
不混淆的,如

android.app.backup.BackupAgentHelper

android.preference.Preference

com.android.vending.licensing.ILicensingService

Java序列化方法,系統序列化需要固定的方法。

枚舉 ,系統需要處理枚舉的固定方法。

本地方法,不能修改本地方法名

annotations 注釋

資料庫驅動

有些resource 文件

用到反射的地方

如何實施

現在的系統已經配置為混淆時候會保留

Android系統組件

自定義View

Android Parcelable

Android R 文件

Android Parcelable

枚舉

各個開發人員必須檢查自己的code 是否用到反射
,和其他不能混淆的地方。告訴我來修改配置文件(已經保留的就不需要了)

目前系統部檢查的第三方庫為

-dontwarn
android.support.**

-dontwarn
com.tencent.**

-dontwarn
org.dom4j.**

-dontwarn
org.slf4j.**

-dontwarn
org.http.mutipart.**

-dontwarn
org.apache.**

-dontwarn
org.apache.log4j.**

-dontwarn
org.apache.commons.logging.**

-dontwarn
org.apache.commons.codec.binary.**

-dontwarn
weibo4android.**

proguard 參數

-include {filename}
從給定的文件中讀取配置參數

-basedirectory {directoryname}

指定基礎目錄為以後相對的檔案名稱

-injars {class_path}
指定要處理的應用程序jar,war,ear和目錄

-outjars {class_path}

指定處理完後要輸出的jar,war,ear和目錄的名稱

-libraryjars {classpath}

指定要處理的應用程序jar,war,ear和目錄所需要的程序庫文件

-

指定不去忽略非公共的庫類。

-

指定不去忽略包可見的庫類的成員。

保留選項

-keep {Modifier} {class_specification}

保護指定的類文件和類的成員

-keepclassmembers {modifier}
{class_specification}
保護指定類的成員,如果此類受到保護他們會保護的更好

-keepclasseswithmembers
{class_specification}
保護指定的類和類的成員,但條件是所有指定的類和類成員是要存在。

-keepnames {class_specification}

保護指定的類和類的成員的名稱(如果他們不會壓縮步驟中刪除)

-keepclassmembernames
{class_specification}
保護指定的類的成員的名稱(如果他們不會壓縮步驟中刪除)

-keepclasseswithmembernames
{class_specification}
保護指定的類和類的成員的名稱,如果所有指定的類成員出席(在壓縮步驟之後)

-printseeds {filename}

列出類和類的成員-keep選項的清單,標准輸出到給定的文件

壓縮

-dontshrink
不壓縮輸入的類文件

-printusage
{filename}

-whyareyoukeeping {class_specification}

優化

-dontoptimize
不優化輸入的類文件

-assumenosideeffects
{class_specification}
優化時假設指定的方法,沒有任何副作用

-allowaccessmodification

優化時允許訪問並修改有修飾符的類和類的成員

混淆

-dontobfuscate
不混淆輸入的類文件

-printmapping
{filename}

-applymapping {filename}

重用映射增加混淆

-obfuscationdictionary {filename}

使用給定文件中的關鍵字作為要混淆方法的名稱

-overloadaggressively

混淆時應用侵入式重載

-useuniqueclassmembernames

確定統一的混淆類的成員名稱來增加混淆

-flattenpackagehierarchy {package_name}

重新包裝所有重命名的包並放在給定的單一包中

-repackageclass {package_name}

重新包裝所有重命名的類文件中放在給定的單一包中

-dontusemixedcaseclassnames

混淆時不會產生形形色色的類名

-keepattributes {attribute_name,...}
保護給定的可選屬性,例如LineNumberTable,
LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature,
and InnerClasses.

-renamesourcefileattribute {string}

設置源文件中給定的字元串常量

解決export打包的報錯

這個時候export提示「conversion to Dalvik
format failed with error
1」錯誤,網上說法有好多種,最後我還是把proguard從4.4升級到4.8就解決了。

升級辦法很簡單,就是把android
sdk目錄下的tool/proguard目錄覆蓋一下即可。

打包出來的程序如何調試

一旦打包出來,就不能用eclipse的logcat去看了,這里可以用android
sdk中ddms.bat的tool來看,一用就發現和logcat其實還是一個東西,就是多了個設備的選擇。

使用 gson 需要的配置

當Gson用到了泛型就會有報錯,這個真給郁悶了半天,提示「Missing
type
parameter」。最後找到一個資料給了一個解決辦法,參考:http://stackoverflow.com/questio ...
sing-type-parameter。

另外我又用到了JsonObject,提交的Object裡面的members居然被改成了a。所以上面給的東西還不夠,還要加上

# 用到自己拼接的JsonObject

-keep class com.google.gson.JsonObject
{ *; }

個人建議減少這些依賴包混淆帶來的麻煩,乾脆都全部保留不混淆。例如

-keep class com.badlogic.** { *;
}

-keep class * implements
com.badlogic.gdx.utils.Json*

-keep class com.google.** { *;
}

使用libgdx需要的配置

驗證打包效果

利用了apktool的反編譯工具,把打包文件又解壓了看了一下,如果包路徑、類名、變數名、方法名這些變化和你期望一致,那就OK了。命令:

apktool.bat d xxx.apk
destdir

配置實例

-injars
androidtest.jar【jar包所在地址】

-outjars
out【輸出地址】

-libraryjars
'D:\android-sdk-windows\platforms\android-9\android.jar'
【引用的庫的jar,用於解析injars所指定的jar類】

-optimizationpasses
5

-dontusemixedcaseclassnames
【混淆時不會產生形形色色的類名 】

-
【指定不去忽略非公共的庫類。 】

-dontpreverify
【不預校驗】

-verbose

-optimizations
!code/simplification/arithmetic,!field/*,!class/merging/*
【優化】

-keep public class * extends
android.app.Activity【不進行混淆保持原樣】

-keep public class * extends
android.app.Application

-keep public class * extends
android.app.Service

-keep public class * extends
android.content.BroadcastReceiver

-keep public class * extends
android.content.ContentProvider

-keep public class * extends
android.app.backup.BackupAgentHelper

-keep public class * extends
android.preference.Preference

-keep public class
com.android.vending.licensing.ILicensingService

-keep public abstract interface
com.asqw.android.Listener{

public protected ;
【所有方法不進行混淆】

}

-keep public class
com.asqw.android{

public void Start(java.lang.String);
【對該方法不進行混淆】

}

-keepclasseswithmembernames class * {
【保護指定的類和類的成員的名稱,如果所有指定的類成員出席(在壓縮步驟之後)】

native ;

}

-keepclasseswithmembers class * {
【保護指定的類和類的成員,但條件是所有指定的類和類成員是要存在。】

public (android.content.Context,
android.util.AttributeSet);

}

-keepclasseswithmembers class *
{

public (android.content.Context,
android.util.AttributeSet, int);

}

-keepclassmembers class * extends
android.app.Activity {【保護指定類的成員,如果此類受到保護他們會保護的更好


public void
*(android.view.View);

}

-keepclassmembers enum *
{

public static **[]
values();

public static **
valueOf(java.lang.String);

}

-keep class * implements
android.os.Parcelable {【保護指定的類文件和類的成員】

public static final
android.os.Parcelable$Creator *;

}

//不混淆指定包下的類

-keep class
com.aspire.**

F. Java 什麼是註解及註解原理詳細介紹

1、註解是針對Java編譯器的說明。

可以給Java包、類型(類、介面、枚舉)、構造器、方法、域、參數和局部變數進行註解。Java編譯器可以根據指令來解釋註解和放棄註解,或者將註解放到編譯後的生成的class文件中,運行時可用。

2、註解和註解類型

註解類型是一種特殊的介面類型,註解是註解註解類型的一個實例。

註解類型也有名稱和成員,註解中包含的信息採用鍵值對形式,可以有0個或多個。

3、Java中定義的一些註解:

@Override 告訴編譯器這個方法要覆蓋一個超類方法,防止程序員覆蓋出錯。

@Deprecated 這個標識方法或類(介面等類型)過期,警告用戶不建議使用。

@SafeVarargs JDK7新增,避免可變參數在使用泛型化時候警告」執行時期無法具體確認參數類型「,當然,也可以用@SuppressWarnings來避免檢查,顯然後者的抑制的范圍更大。

@SuppressWarnings(value={"unchecked"}) 抑制編譯警告,應用於類型、構造器、方法、域、參數以及局部變數。 value是類型數組,有效取值為:

all, to suppress all warnings

boxing, to suppress warnings relative to boxing/unboxing operations

cast, to suppress warnings relative to cast operations

dep-ann, to suppress warnings relative to deprecated annotation

deprecation, to suppress warnings relative to deprecation

fallthrough, to suppress warnings relative to missing breaks in switch statements

finally, to suppress warnings relative to finally block that don't return

hiding, to suppress warnings relative to locals that hide variable

incomplete-switch, to suppress warnings relative to missing entries in a switch statement (enum case)

javadoc, to suppress warnings relative to javadoc warnings

nls, to suppress warnings relative to non-nls string literals

null, to suppress warnings relative to null analysis

rawtypes, to suppress warnings relative to usage of raw types

restriction, to suppress warnings relative to usage of discouraged or forbidden references

serial, to suppress warnings relative to missing serialVersionUID field for a serializable class

static-access, to suppress warnings relative to incorrect static access

static-method, to suppress warnings relative to methods that could be declared as static

super, to suppress warnings relative to overriding a method without super invocations

synthetic-access, to suppress warnings relative to unoptimized access from inner classes

unchecked, to suppress warnings relative to unchecked operations

unqualified-field-access, to suppress warnings relative to field access unqualified

unused, to suppress warnings relative to unused code and dead code

4、註解的定義

使用 @interface 關鍵字聲明一個註解

public @interface MyAnnotation1

註解中可以定義屬性

String name default 「defval」;

value是註解中的特殊屬性

註解中定義的屬性如果名稱為 value, 此屬性在使用時可以省寫屬性名

例如,聲明一個註解:

@Retention(RetentionPolicy.RUNTIME)

public @interface MyAnno1 {

String msg();

int value();

}

G. 深入理解Java中為什麼內部類可以訪問外部類的成員

內部類簡介

雖然Java是一門相對比較簡單的編程語言,但是對於初學者, 還是有很多東西感覺雲里霧里,
理解的不是很清晰。內部類就是一個經常讓初學者感到迷惑的特性。 即使現在我自認為Java學的不錯了,
但是依然不是很清楚。其中一個疑惑就是為什麼內部類對象可以訪問外部類對象中的成員(包括成員變數和成員方法)?
早就想對內部類這個特性一探究竟了,今天終於抽出時間把它研究了一下。

內部類就是定義在一個類內部的類。定義在類內部的類有兩種情況:一種是被static關鍵字修飾的, 叫做靜態內部類,
另一種是不被static關鍵字修飾的, 就是普通內部類。 在下文中所提到的內部類都是指這種不被static關鍵字修飾的普通內部類。
靜態內部類雖然也定義在外部類的裡面, 但是它只是在形式上(寫法上)和外部類有關系,
其實在邏輯上和外部類並沒有直接的關系。而一般的內部類,不僅在形式上和外部類有關系(寫在外部類的裡面), 在邏輯上也和外部類有聯系。
這種邏輯上的關系可以總結為以下兩點:

1 內部類對象的創建依賴於外部類對象;

2 內部類對象持有指向外部類對象的引用。

上邊的第二條可以解釋為什麼在內部類中可以訪問外部類的成員。就是因為內部類對象持有外部類對象的引用。但是我們不禁要問, 為什麼會持有這個引用? 接著向下看, 答案在後面。

通過反編譯位元組碼獲得答案

在源代碼層面, 我們無法看到原因,因為Java為了語法的簡介, 省略了很多該寫的東西, 也就是說很多東西本來應該在源代碼中寫出, 但是為了簡介起見, 不必在源碼中寫出,編譯器在編譯時會加上一些代碼。 現在我們就看看Java的編譯器為我們加上了什麼?
首先建一個工程TestInnerClass用於測試。 在該工程中為了簡單起見, 沒有創建包, 所以源代碼直接在默認包中。在該工程中, 只有下面一個簡單的文件。

?

1
2
3
4
5
6
7
8
9

public class Outer {
int outerField = 0;

class Inner{
void InnerMethod(){
int i = outerField;
}
}
}

該文件很簡單, 就不用過多介紹了。 在外部類Outer中定義了內部類Inner, 並且在Inner的方法中訪問了Outer的成員變數outerField。
雖然這兩個類寫在同一個文件中, 但是編譯完成後, 還是生成各自的class文件:

這里我們的目的是探究內部類的行為, 所以只反編譯內部類的class文件Outer$Inner.class 。 在命令行中, 切換到工程的bin目錄, 輸入以下命令反編譯這個類文件:

?

1

javap -classpath . -v Outer$Inner

-classpath . 說明在當前目錄下尋找要反編譯的class文件
-v 加上這個參數輸出的信息比較全面。包括常量池和方法內的局部變數表, 行號, 訪問標志等等。
注意, 如果有包名的話, 要寫class文件的全限定名, 如:

?

1

javap -classpath . -v com..Outer$Inner

反編譯的輸出結果很多, 為了篇幅考慮, 在這里我們省略了常量池。 下面給出除了常量池之外的輸出信息。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

{
final Outer this$0;
flags: ACC_FINAL, ACC_SYNTHETIC

Outer$Inner(Outer);
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #10 // Field this$0:LOuter;
5: aload_0
6: invokespecial #12 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LOuter$Inner;

void InnerMethod();
flags:
Code:
stack=1, locals=2, args_size=1
0: aload_0
1: getfield #10 // Field this$0:LOuter;
4: getfield #20 // Field Outer.outerField:I
7: istore_1
8: return
LineNumberTable:
line 7: 0
line 8: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this LOuter$Inner;
8 1 1 i I
}</init>

首先我們會看到, 第一行的信息如下:
?

1

final Outer this$0;

這句話的意思是, 在內部類Outer$Inner中, 存在一個名字為this$0 , 類型為Outer的成員變數, 並且這個變數是final的。
其實這個就是所謂的「在內部類對象中存在的指向外部類對象的引用」。但是我們在定義這個內部類的時候, 並沒有聲明它,
所以這個成員變數是編譯器加上的。

雖然編譯器在創建內部類時為它加上了一個指向外部類的引用, 但是這個引用是怎樣賦值的呢?畢竟必須先給他賦值,它才能指向外部類對象。下面我們把注意力轉移到構造函數上。 下面這段輸出是關於構造函數的信息。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Outer$Inner(Outer);
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #10 // Field this$0:LOuter;
5: aload_0
6: invokespecial #12 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LOuter$Inner;</init>

我們知道, 如果在一個類中, 不聲明構造方法的話, 編譯器會默認添加一個無參數的構造方法。 但是這句話在這里就行不通了, 因為我們明明看到, 這個構造函數有一個構造方法, 並且類型為Outer。 所以說,
編譯器會為內部類的構造方法添加一個參數, 參數的類型就是外部類的類型。
下面我們看看在構造參數中如何使用這個默認添加的參數。 我們來分析一下構造方法的位元組碼。 下面是每行位元組碼的意義:
aload_0 :
將局部變數表中的第一個引用變數載入到操作數棧。 這里有幾點需要說明。
局部變數表中的變數在方法執行前就已經初始化完成;局部變數表中的變數包括方法的參數;成員方法的局部變數表中的第一個變數永遠是this;操作數棧就是
執行當前代碼的棧。所以這句話的意思是: 將this引用從局部變數表載入到操作數棧。

aload_1:
將局部變數表中的第二個引用變數載入到操作數棧。 這里載入的變數就是構造方法中的Outer類型的參數。
putfield #10 // Field this$0:LOuter;
使用操作數棧頂端的引用變數為指定的成員變數賦值。 這里的意思是將外面傳入的Outer類型的參數賦給成員變數this$0 。
這一句putfield位元組碼就揭示了, 指向外部類對象的這個引用變數是如何賦值的。
下面幾句位元組碼和本文討論的話題無關, 只做簡單的介紹。 下面幾句位元組碼的含義是: 使用this引用調用父類(Object)的構造方法然後返回。
用我們比較熟悉的形式翻譯過來, 這個內部類和它的構造函數有點像這樣: (注意, 這里不符合Java的語法, 只是為了說明問題)

?

1
2
3
4
5
6
7
8

class Outer$Inner{
final Outer this$0;

public Outer$Inner(Outer outer){
this.this$0 = outer;
super();
}
}

說到這里, 可以推想到, 在調用內部類的構造器初始化內部類對象的時候, 編譯器默認也傳入外部類的引用。 調用形式有點像這樣: (注意, 這里不符合java的語法, 只是為了說明問題)
vcq9ysfP4M2stcShoyDU2sTasr//wOC1xLPJ1LGx5MG/b3V0ZXJGaWVsZKOsIM/Cw++NDQtcSjugo8YnI+Cgo8cHJlIGNsYXNzPQ=="brush:java;">
void InnerMethod();
flags:
Code:
stack=1, locals=2, args_size=1
0: aload_0
1: getfield #10 // Field this$0:LOuter;
4: getfield #20 // Field
Outer.outerField:I
7: istore_1
8: return
getfield #10 // Field this$0:LOuter;
將成員變數this$0載入到操作數棧上來
getfield #20 // Field Outer.outerField:I
使用上面載入的this$0引用, 將外部類的成員變數outerField載入到操作數棧
istore_1
將操作數棧頂端的int類型的值保存到局部變數表中的第二個變數上(注意, 第一個局部變數被this佔用,
第二個局部變數是i)。操作數棧頂端的int型變數就是上一步載入的outerField變數。 所以, 這句位元組碼的含義就是:
使用outerField為i賦值。
上面三步就是內部類中是如何通過指向外部類對象的引用, 來訪問外部類成員的。

文章寫到這里, 相信讀者對整個原理就會有一個清晰的認識了。 下面做一下總結:
本文通過反編譯內部類的位元組碼, 說明了內部類是如何訪問外部類對象的成員的,除此之外, 我們也對編譯器的行為有了一些了解, 編譯器在編譯時會自動加上一些邏輯, 這正是我們感覺困惑的原因。

關於內部類如何訪問外部類的成員, 分析之後其實也很簡單, 主要是通過以下幾步做到的:
1 編譯器自動為內部類添加一個成員變數, 這個成員變數的類型和外部類的類型相同, 這個成員變數就是指向外部類對象的引用;
2 編譯器自動為內部類的構造方法添加一個參數, 參數的類型是外部類的類型, 在構造方法內部使用這個參數為1中添加的成員變數賦值;
3 在調用內部類的構造函數初始化內部類對象時, 會默認傳入外部類的引用。

H. java,求高手講解一下一個枚舉的例子

結果:Mr. John Doe
轉的。枚舉是一個特殊的類。

枚舉類型是JDK5.0的新特徵 。Sun引進了一個全新的關鍵字enum來定義一個枚舉類 。下面就是一個典型枚舉類型的定義:

Java代碼:

public enum Color{
RED,BLUE,BLACK,YELLOW,GREEN
}

顯然,enum很像特殊的class,實際上enum聲明定義的類型就是一個類 。 而這些類都是類庫中Enum類的子類(java.lang.Enum) 。它們繼承了這個Enum中的許多有用的方法 。我們對代碼編譯之後發現,編譯器將enum類型單獨編譯成了一個位元組碼文件:Color.class 。

Color位元組碼代碼

final enum hr.test.Color {
// 所有的枚舉值都是類靜態常量
public static final enum hr.test.Color RED;
public static final enum hr.test.Color BLUE;
public static final enum hr.test.Color BLACK;
public static final enum hr.test.Color YELLOW;
public static final enum hr.test.Color GREEN;
private static final synthetic hr.test.Color〔〕 ENUM$VALUES;
// 初始化過程,對枚舉類的所有枚舉值對象進行第一次初始化
static {
0 new hr.test.Color 〔1〕
3 p
4 ldc 〔16〕 //把枚舉值字元串「RED」壓入操作數棧
6 iconst_0 // 把整型值0壓入操作數棧
7 invokespecial hr.test.Color(java.lang.String, int) 〔17〕 //調用Color類的私有構造器創建Color對象RED
10 putstatic hr.test.Color.RED : hr.test.Color 〔21〕 //將枚舉對象賦給Color的靜態常量RED 。
。.. 。.. 。.. 枚舉對象BLUE等與上同
102 return
};
// 私有構造器,外部不可能動態創建一個枚舉類對象(也就是不可能動態創建一個枚舉值) 。
private Color(java.lang.String arg0, int arg1){
// 調用父類Enum的受保護構造器創建一個枚舉對象
3 invokespecial java.lang.Enum(java.lang.String, int) 〔38〕
};
public static hr.test.Color〔〕 values();
// 實現Enum類的抽象方法
public static hr.test.Color valueOf(java.lang.String arg0);
}

下面我們就詳細介紹enum定義的枚舉類的特徵及其用法 。(後面均用Color舉例)

1、Color枚舉類就是class,而且是一個不可以被繼承的final類 。

其枚舉值(RED,BLUE. 。.)都是Color類型的類靜態常量, 我們可以通過下面的方式來得到Color枚舉類的一個實例:

Color c=Color.RED;

注意:這些枚舉值都是public static final的,也就是我們經常所定義的常量方式,因此枚舉類中的枚舉值最好全部大寫 。

2、即然枚舉類是class,當然在枚舉類型中有構造器,方法和數據域 。

但是,枚舉類的構造器有很大的不同:

(1) 構造器只是在構造枚舉值的時候被調用 。

Java代碼:

enum Color{
RED(255,0,0),BLUE(0,0,255),BLACK(0,0,0),YELLOW(255,255,0),GREEN(0,255,0);
//構造枚舉值,比如RED(255,0,0)
private Color(int rv,int gv,int bv){
this.redValue=rv;
this.greenValue=gv;
this.blueValue=bv;
}
public String toString(){ //覆蓋了父類Enum的toString()
return super.toString()+「(」+redValue+「,」+greenValue+「,」+blueValue+「)」;
}
private int redValue; //自定義數據域,private為了封裝 。
private int greenValue;
private int blueValue;
}

(2) 構造器只能私有private,絕對不允許有public構造器 。 這樣可以保證外部代碼無法新構造枚舉類的實例 。這也是完全符合情理的,因為我們知道枚舉值是public static final的常量而已 。 但枚舉類的方法和數據域可以允許外部訪問 。

Java代碼:

public static void main(String args〔〕)
{
// Color colors=new Color(100,200,300); //wrong
Color color=Color.RED;
System.out.println(color); // 調用了toString()方法
}

3、所有枚舉類都繼承了Enum的方法,下面我們詳細介紹這些方法 。

(1) ordinal()方法: 返回枚舉值在枚舉類種的順序 。這個順序根據枚舉值聲明的順序而定 。

Color.RED.ordinal(); //返回結果:0
Color.BLUE.ordinal(); //返回結果:1

(2) compareTo()方法: Enum實現了java.lang.Comparable介面,因此可以比較象與指定對象的順序 。Enum中的compareTo返回的是兩個枚舉值的順序之差 。當然,前提是兩個枚舉值必須屬於同一個枚舉類,否則會拋出ClassCastException()異常 。(具體可見源代碼)

Color.RED.compareTo(Color.BLUE); //返回結果 -1

(3) values()方法: 靜態方法,返回一個包含全部枚舉值的數組 。

Color〔〕 colors=Color.values();
for(Color c:colors){
System.out.print(c+「,」);
}//返回結果:RED,BLUE,BLACK YELLOW,GREEN,

(4) toString()方法: 返回枚舉常量的名稱 。

Color c=Color.RED;
System.out.println(c);//返回結果: RED

(5) valueOf()方法: 這個方法和toString方法是相對應的,返回帶指定名稱的指定枚舉類型的枚舉常量 。

Color.valueOf(「BLUE」); //返回結果: Color.BLUE

(6) equals()方法: 比較兩個枚舉類對象的引用 。

Java代碼:

//JDK源代碼:
public final boolean equals(Object other) {
return this==other;
}

4、枚舉類可以在switch語句中使用 。

Java代碼:

Color color=Color.RED;
switch(color){
case RED: System.out.println(「it『s red」);break;
case BLUE: System.out.println(「it』s blue」);break;
case BLACK: System.out.println(「it『s blue」);break;
}

I. java中什麼是bridge method

Java中的bridge method又叫橋接方法
橋接方法是 JDK 1.5 引入泛型後,為了使Java的泛型方法生成的位元組碼和 1.5 版本前的位元組碼相兼容,由編譯器自動生成的方法。
我們可以通過Method.isBridge()方法來判斷一個方法是否是橋接方法,在位元組碼中橋接方法會被標記為ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用於說明這個方法是由編譯生成的橋接方法,ACC_SYNTHETIC說明這個方法是由編譯器生成,並且不會在源代碼中出現。

閱讀全文

與syntheticjava相關的資料

熱點內容
蘋果手機dcim文件夾顯示不出來 瀏覽:430
如何壓縮文件夾聯想電腦 瀏覽:583
程序員的學習之旅 瀏覽:440
apkdb反編譯 瀏覽:922
雪花演算法為什麼要二進制 瀏覽:825
在文檔中打開命令行工具 瀏覽:608
android圖標尺寸規范 瀏覽:369
python實用工具 瀏覽:208
流量計pdf 瀏覽:936
科東加密認證價格 瀏覽:532
dos命令讀文件 瀏覽:996
成為程序員需要什麼學歷 瀏覽:672
pdf農葯 瀏覽:228
canal加密 瀏覽:497
日本安卓系統和中國有什麼區別 瀏覽:137
linux命令行修改文件 瀏覽:838
從編譯和解釋的角度看 瀏覽:649
徐志摩pdf 瀏覽:651
夏天解壓球視頻 瀏覽:304
全封閉壓縮機qd91h 瀏覽:668