??xml version="1.0" encoding="utf-8" standalone="yes"?> 1Q获得类型类 可以看到Q对象a是A的一个实例,A某一个类Q在if语句中用a.getClass()q回的结果正是A的类型类Q在Java中表CZ个特定类型的cdcd以用“cd.class”的方式获得,因ؓa.getClass()获得是A的类型类Q也是A.classQ因此上面的代码执行的结果就是打印出“equal”。特别注意的是,cdcL一一对应的,父类的类型类和子cȝcdcL不同的,因此Q假设A是B的子c,那么如下的代码将得到“unequal”的输出: 因此Q如果你知道一个实例,那么你可以通过实例?#8220;getClass()”Ҏ(gu)获得该对象的cdc,如果你知道一个类型,那么你可以?#8220;.class”的方法获得该cd的类型类?br />
除了q些以外Q利用类型类q可以反该cd中的所有属性和Ҏ(gu)。在Java中所有的属性信息都用Field表示Q所有的Ҏ(gu)信息都用Method表示Q这辆各c都是java.lang.reflect包中的类。在Class中提供了4个相关的Ҏ(gu)获得cd的属性: 对于Ҏ(gu)也有cM的函数即Q?br />
getMethods():Method[] 4Q创建实?br />
利用Class对象可以创徏一个类型的实例。如果一个类型拥有无参数的构造函敎ͼ那么可以单地调用Class.newInstance()Ҏ(gu)创徏一个实例。如果该cd没有无参数的构造函敎ͼ或者你希望是用某个有参数的构造函敎ͼ那么可以首先使用getConstructors()、getConstructor(Class[] parameterTypes)和getDeclaredConstructors()、getDeclaredConstructor(Class[] parameterTypes)获得构造函敎ͼq两个方法的q回值都使Constructorcd。特别注意的是,构造函C能承,因此你调用getConstructor也只能返回这个类型中定义的所有公有构造函数?br />
我们知道在Java中一切都是对象,我们一般所使用的对象都直接或间接承自ObjectcRObjectcM包含一个方法名叫getClassQ利用这个方法就可以获得一个实例的cdcR类型类指的是代表一个类型的c,因ؓ一切皆是对象,cd也不例外Q在Java使用cdcL表示一个类型。所有的cdc都是Classcȝ实例。例如,有如下一D代码:
A a = new A();
if(a.getClass()==A.class)
System.out.println("equal");
else System.out.println("unequal");
A a = new A();
if(a.getClass()==B.class)
System.out.println("equal");
else System.out.println("unequal");
2Q获得类型的信息
在获得类型类之后Q你可以调用其中的一些方法获得类型的信息了,主要的方法有Q?br />
getName():StringQ获得该cd的全U名U?br />
getSuperClass():ClassQ获得该cd的直接父c,如果该类型没有直接父c,那么q回null?br />
getInterfaces():Class[]Q获得该cd实现的所有接口?br />
isArray():booleanQ判断该cd是否是数l?br />
isEnum():booleanQ判断该cd是否是枚丄型?br />
isInterface():booleanQ判断该cd是否是接口?br />
isPrimitive():booleanQ判断该cd是否是基本类型,x否是intQbooleanQdouble{等?br />
isAssignableFrom(Class cls):booleanQ判断这个类型是否是cdcls的父Q祖先)cLӞ先Q接口?br />
getComponentType():ClassQ如果该cd是一个数l,那么q回该数l的lgcd?br />
此外q可以进行类型{换这cȝ操作Q主要方法有Q?br />
asSubclass(Class clazz):ClassQ将q个cd转换至clazzQ如果可以{换,那么Lq回clazzq个引用Q否则抛出异常?br />
cast(Object obj):ObjectQ将obj强制转换个类型类代表的类型,不能转换的话抛出异常?/span>
getField(String name):Field
getFields():Field[]
getDeclaredField(String name):Field
getDeclaredFields():Field[]
其中getField用于q回一个指定名U的属性,但是q个属性必L公有的,q个属性可以在父类中定义。如果是U有属性或者是保护属性,那么都会抛出异常提示找不到这个属性。getFields则是q回cd中的所有公有属性,所有的U有属性和保护属性都找不到。getDeclaredField获得在这个类型的声明中定义的指定名称的属性,q个属性必L在这个类型的声明中定义,但可以ɿU有和保护的。getDeclaredFields获得在这个类型的声明中定义的所有属性,包括U有和保护的属性都会被q回Q但是所有父cȝ属性都不会被返回。D个例子,先考虑下面两个cȝ声明Q?br />
class A extends B {
public int a1;
private int a2;
}
class B {
public int b1;
private int b2;
}
如果利用A的类型类调用getFieldsQ那么会q回a1和b1两个属性,如果调用getField("a2")则会报错Q如果调用getDeclaredFields则会q回a1和a2Q如果调用getDeclaredField("b1")则会报错?/span>
getMethod(String name, Class ... parameterTypes):Method
getDeclaredMethods():Method[]
getDeclaredMethod(Strubg name, Class ...parameterTypes):Method
不定长参?..是JDK5.0以后新加入的语法。这几个Ҏ(gu)的用法和上面的类|只是在获得特定方法时Q除了要告知Ҏ(gu)的名字,q需要告知方法的参数Q如果没有参敎ͼ那么可以传递nullQ或者空数组Q但是最好的Ҏ(gu)是什么都不写Q编译器会自行解决不定长参数问题?br />
如果要获得所有的属性(Ҏ(gu)Q,包括公有和私有的Q那么就必须利用getDeclareFieldsQgetDeclareMethodsQ方法,然后再利用getSuperClass的方法获得父c,然后递归下去?br />
3Q属性和Ҏ(gu)
所有的属性都使用Field表示Q所有的Ҏ(gu)都用Method表示。利用Field和Method可以获得属性和Ҏ(gu)的信息,甚至执行是获取、修改属性值和调用Ҏ(gu)?br />
对于属性,主要有以下方法可以用:
getType():ClassQ获得该属性的cd?br />
getName():StringQ获得属性名U?br />
isAccessible():booleanQ判断该属性是否是可以讉K的,通常U有和保护的cd都是不可以访问的?br />
get(Object obj):ObjectQ获得实例obj的属性|如果该实例的cd中不包含q个属性,那么׃报错?br />
set(Object obj, Object value)Q设|该实例的属性?br />
setAccessible(boolean flag)Q设|该属性是否可以访问,如果你调用get和setҎ(gu)Q那么有可能会引发访问权限的错误Q这个时候你可以调用setAccessibleҎ(gu)使得该属性可以访问。例如下面的代码Q?br />
A a = new A();
Field f = A.class.getDeclaredField("a2");
f.setAccessibe(true);
System.out.println(f.get(a));
f.set(a,12);
System.out.println(f.get(a));
如果Ud中间的f.setAccessibe(true);那么代码会报错,反之输出0 12?br />
对于属性而言Q如果该属性的cd是基本类型,那么q可以用一些便L(fng)set和get操作Q例如getIntQsetInt什么的Q你可以Ҏ(gu)自己的需要调用相应的Ҏ(gu)?br />
对于Ҏ(gu)Q可以有以下的方法:
getName():StringQ获得方法的名字?br />
getReturnType():ClassQ获得方法的q回值类型?br />
getParameterTypes():Class[]Q获得方法的参数cd?br />
isAccessible():booleanQ判断该Ҏ(gu)是否是可以访问的?br />
setAccessible(boolean flag)Q设|该Ҏ(gu)是否可以讉K?br />
invoke(Object obj, Object... args):ObjectQ调用实例obj的相应方法,其参数由argsl定Q如果没有参数那么可以什么都不写?br />
getExceptionTypes():Class[]Q获得该Ҏ(gu)可能抛出的异常类cd?br />
q几个方法的含义和用法都和Field的类|q里不再赘述?/span>
Constructor的用方法和Method的类|它也存在getParameterTypes()Ҏ(gu)和getExceptionTypes()Ҏ(gu)Q不同的是,它用newInstance(Object... args)来调用一个构造函敎ͼ注意newInstance不需要实例对象,因ؓq个时候你q没创徏出来q个实例呢?/span>
]]>
栈的优势是,存取速度比堆要快Q仅ơ于寄存器,栈数据可以共享。但~点是,存在栈中的数据大与生存期必L定的,~Z灉|性。栈中主要存放一些基本类型的变量Q?int, short, long, byte, float, double, boolean, charQ和对象句柄?
栈有一个很重要的特D性,是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3Q?
~译器先处理int a = 3Q首先它会在栈中创徏一个变量ؓa的引用,然后查找栈中是否?q个|如果没找刎ͼ将3存放q来Q然后将a指向3。接着处理int b = 3Q在创徏完b的引用变量后Q因为在栈中已经?q个|便将b直接指向3。这P出Ca与b同时均指?的情c?
q时Q如果再令a=4Q那么编译器会重新搜索栈中是否有4|如果没有Q则?存放q来Qƈ令a指向4Q如果已l有了,则直接将a指向q个地址。因此a值的改变不会影响到b的倹{?
要注意这U数据的׃n与两个对象的引用同时指向一个对象的q种׃n是不同的Q因U情况a的修改ƈ不会影响到b, 它是q译器完成的,它有利于节省I间。而一个对象引用变量修改了q个对象的内部状态,会媄响到另一个对象引用变量?
String是一个特D的包装cL据。可以用Q?
String str = new String("abc");
String str = "abc";
两种的Ş式来创徏Q第一U是用new()来新建对象的Q它会在存放于堆中。每调用一ơ就会创Z个新的对象。而第二种是先在栈中创Z个对Stringcȝ对象引用变量strQ然后查找栈中有没有存放"abc"Q如果没有,则将"abc"存放q栈Qƈ令str指向”abc”Q如果已l有”abc” 则直接ostr指向“abc”?
比较c里面的数值是否相{时Q用equals()Ҏ(gu)Q当试两个包装cȝ引用是否指向同一个对象时Q用==Q下面用例子说明上面的理论?
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的?
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一ơ生成一个?
因此用第二种方式创徏多个”abc”字符?在内存中其实只存在一个对象而已. q种写法有利与节省内存空? 同时它可以在一定程度上提高E序的运行速度Q因为JVM会自动根据栈中数据的实际情况来决定是否有必要创徏新对象。而对于String str = new String("abc")Q的代码Q则一概在堆中创徏新对象,而不其字符串值是否相{,是否有必要创建新对象Q从而加重了E序的负担?
另一斚w, 要注? 我们在用诸如String str = "abc"Q的格式定义cLQL惛_然地认ؓQ创ZStringcȝ对象str。担心陷阱!对象可能q没有被创徏Q而可能只是指向一个先前已l创建的对象。只有通过new()Ҏ(gu)才能保证每次都创Z个新的对象?
׃Stringcȝimmutable性质Q当String变量需要经常变换其值时Q应该考虑使用StringBufferc,以提高程序效率?
]]>
]]>
作?/span>
javaworld.com">John D. Mitchell
摘要
q个技巧提供了一个实例,它可以利用静态内部类为?zhn)的类d独立的、可选的辅助功能Q例如测试和CZ代码{。(
500
?/span>
Q?/span>
学会了这个技巧,(zhn)便可以?/span>
静态内部类
的用加入到(zhn)的
Java
使用技巧集锦当中。静态内部类是在另一个类的定义中q行定义Qƈ且标Cؓ静态的cR我向(zhn)展CZ个实例,它利用静态内部类向另一个类中添加测试代码?/span>
静态内部类在概念和实现上都十分单,基本上来说就是在(zhn)的ȝ中定义一个静态类Q?/span>
public class TestDebug
{
private double num;
public TestDebug(double in)
{
num = in;
}
public void output()
{
System.out.println(num);
}
public static class Test
{
public static void main(String[] args)
{
TestDebug td = new TestDebug(3.9);
td.output();
}
}
}
说到向?zhn)主要的类中添加辅助代码,其中最重要的一点就是静态内部类被编译到一个单独的
.class
文g中,q个文g独立于它的外部类。例如,如果外部cd?/span>
Foo
Q而它的一个内部类?/span>
Test
Q那么这个内部类被~译?/span>
Foo$Test.class
文g?/span>
.class
文g的分L味着(zhn)可以将辅助的嵌套代码与主要的外部类牢固地捆l在一赗它们在同一个源文g中,内部cȝ是在外部类?/span>
内部
。?zhn)无需再付ZQ何发布或q行时的开销。真!例如Q如果辅助代码只是用于调试,那么(zhn)只需发布
Foo.class
文g而将
Foo$Test.class
文g留下卛_?/span>
我将q个技巧主要用于编写外部类的演CZ码、错误调试代码,以及q行单元试实现c行为的自动验证。(当然Q做Z个勤奋的开发h员,我准备将试代码转化成单元测试。)
注意Q要执行
TestDebug
.class
cȝ
main()
Ҏ(gu)Q请使用下面的命令:
% java TestDebug$Test
如果(zhn)正在用的命o解释E序Q?/span>
shell
Q把
??/span>
做ؓ一个保留字Q那么?zhn)应该使用下面的命令?/span>
% java TestDebug\$Test
q有一点十分有:静态内部类Ҏ(gu)定义可以讉K外部cȝ保护域和U有域。这件事可以说既有利也有弊。因为?zhn)可能在不l意间就破坏了外部类的保护域和私有域Q从而违反了它的装性,所以请心对待Q这一功能最恰当的应用就是编写类?/span>
白盒
试E序Q-因ؓq样可以引入一些利用通常?i>黑盒试很难引入的问题(黑盒试不能讉K对象的内部状态)?/span>
l论
通过使用静态内部类Q?zhn)可以l?zhn)的系l添加辅助功能,以便完成诸如试之类的工作,而对正式发布的品不会带来Q何不利媄响?/span>
J2EE开发中大量的专业羃略语很是让hqhQ尤其是跟一些高手讨论问题的时候,三分钟就被h家满口的专业术语h了,PO VO BO DTO POJO DAOQ一大堆的就来了Q听q老罗对这U现象的批判的朋友会会心一W)?/p>
首先声明偶也不是什么高手,以下ȝ都是自己的体会。不对之处请(zhn)多指教?/p>
POQ?br />persistant object持久对象
最形象的理解就是一个PO是数据库中的一条记录?br />好处是可以把一条记录作Z个对象处理,可以方便的{为其它对象?br />
BOQ?br />business object业务对象
主要作用是把业务逻辑装Z个对象。这个对象可以包括一个或多个其它的对象?br />比如一个简历,有教育经历、工作经历、社会关pȝ{?br />我们可以把教育经历对应一个POQ工作经历对应一个POQ社会关pd应一个PO?br />建立一个对应简历的BO对象处理历,每个BO包含q些PO?br />q样处理业务逻辑Ӟ我们可以针对BOd理?br />
VO Q?br />value object值对?br />ViewObject表现层对?br />
主要对应界面昄的数据对象。对于一个WEB面Q或者SWT、SWING的一个界面,用一个VO对象对应整个界面的倹{?br />
DTO Q?br />Data Transfer Object数据传输对象
主要用于q程调用{需要大量传输对象的地方?br />比如我们一张表?00个字D,那么对应的PO有100个属性?br />但是我们界面上只要显C?0个字D,
客户端用WEB service来获取数据,没有必要把整个PO对象传递到客户端,
q时我们可以用只有q?0个属性的DTO来传递结果到客户端,q样也不会暴露服务端表结?到达客户端以后,如果用这个对象来对应界面昄Q那此时它的w䆾p{为VO
POJO Q?br />plain ordinary java object 单java对象
个h感觉POJO是最常见最多变的对象,是一个中间对象,也是我们最常打交道的对象?br />
一个POJO持久化以后就是PO
直接用它传递、传递过E中是DTO
直接用来对应表示层就是VO
DAOQ?br />data access object数据讉K对象
q个大家最熟?zhn)Q和上面几个O区别最大,基本没有互相转化的可能性和必要.
主要用来装Ҏ(gu)据库的访问。通过它可以把POJO持久化ؓPOQ用POl装出来VO、DTO
ȝ下我认ؓ一个对象究竟是什么O要看具体环境Q在不同的层、不同的应用场合Q对象的w䆾也不一P而且对象w䆾的{化也是很自然的。就像你对老婆来说是老公Q对父母来说是子女。设计这些概늚初衷不是Z唬h而是Z更好的理解和处理各种逻辑Q让大家能更好的ȝ面向对象的方式处理问?
大家千万不要陷入q度设计Q大可不必ؓ了设计而设计一定要在代码中区分各个对象。一句话技术是为应用服务的?br />
Ƣ迎指正?br />
A reader asked a question via a comment a couple months ago that I didn't really have an answer for (and had always kind of wondered the same thing). In the original post (which showed how to use JDBC with ColdFusion), I used the following snippet of code:
Class.forName("jdbc.DriverXYZ");
Connection con = DriverManager.getConnection(url,
"myLogin", "myPassword");
and the reader wanted to know what the Class.forName(..)
method did. The most common answer you'll hear is that it loads the database driver, which, while technically true, is shallow. Where does it get loaded? How does it happen? And why?
To answer the question I started with the JavaDoc for the Class.forName() method. According to the documentation, the method:
... attempts to locate, load, and link the class or interfaceI wasn't perfectly clear on what "locate, load, and link" meant, so I did a little digging through the Java Language Specification. According to chapter 12 of the JLS:
Loading refers to the process of finding the binary form of a class or interface type with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a compiler, and constructing, from that binary form, a Class object to represent the class or interface.Next, again according to the JLS, it must be transformed from it's binary representation to something the Java virtual machine can use, this process is called linking. Finally, the class is initialized, which is the process that executes the static initializer and the initializers for static fields declared in the class.
So then back to the original problem, when Class.forName() is called with an argument like this:
Class.forName("org.gjt.mm.mysql.Driver");
the classloader attempts to load and link the Driver
class in the "org.gjt.mm.mysql" package and if successful, the static initializer is run. The MySQL Driver
(download the source code) static initializer looks like this:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
So it calls a static method in the java.sql.DriverManager class which apparently registers a copy of itself when it loads.
So now I understand the where and the how, what about why? To understand the why you have to look at the next line in the initial code example:
Connection con = DriverManager.getConnection(url,
"myLogin", "myPassword");
The DriverManager
class (view DriverManager source here) returns a database connection given a JDBC URL string, a username and a password. In order to create that connection, the DriverManager class has to know which database driver you want to use. It does that by iterating over the array (internally a Vector) of drivers that have registered with it (ie: the registerDriver(Driver driver)
method illustrated above) and calls the acceptsURL(url))
method on each driver in the array, effectively asking the driver to tell it whether or not it can handle the JDBC URL.
So there you have it. Class.forName explained.