finalize方法
它是java.lang.Object类中的方法. 是protected的.
缺省时这个方法是空的. Java运行系统在垃圾回收时在对象被撤销之前调用这个方法. 但由于垃圾单元
回收的时间是不确定的.所以不能过分依赖它(这和c++的析构函数不同).
而只能是"在对象被撤销之前"做一些处理.例如可以在这里关闭构造方法中打开的套接字.
如果要重写该方法.访问属性只能是protected或更低.
///
// Object类
//
// 构造方法:
// public Object()
// 其它方法:
// public final Class<? extends Object> getClass()
// //返回一个对象的运行时类(java.lang.Class 对象)。
// //该 Class 对象是由所表示类的 static synchronized 方法锁定的对象。
// public int hashCode() //返回该对象的哈希码值.用来支持哈希表.
// //哈希函数的约定是:
// // 在程序执行时对同一个对象多次调用该方法应该返回相同的整数.但一个程序
// // 的多次运行中对象的哈希码值可以不同.
// // 如果两个对象根据 equals(Object)方法比较相等. 则两对象的哈希值应该相同.
// public boolean equals(Object obj) //比较两个对象是否相等.
// 但在Object类中实现为判断2者是否引用同一个对象. 所以自己的类需要的时候要覆盖它.
// 自己写 equals 方法时应符合下边的约定:
// 测试this和obj是否引用同一个对象. if(this==obj) return true;
// 测试obj是否为null. if(obj == null) return false;
// 测试this和obj是否属于同一个类. if(getClass != obj.getClass()) return false;
// 再把obj转换为本类类型后. 比较this和obj的所有字段. 如果本类有基类.先比基类部分如:
// if (!super.equals(obj)) return false;
// protected Object clone() throws CloneNotSupportedException //返回一个副本
// public String toString() //返回该对象的字符串表示. 在Object类的实现中返回字符串:
// getClass().getName() + '@' + Integer.toHexString(hashCode())
// public final void notify() //唤醒在此对象监视器上等待的单个线程
// public final void notifyAll() //唤醒在此对象监视器上等待的所有线程
// public final void wait()
// public final void wait(long timeout) throws InterruptedException
// public final void wait(long timeout, int nanos)
// 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法
// 或者超过指定的时间量
// protected void finalize() //当垃圾回收器回收当前对象时.垃圾回收器调用此方法
/
包
用package声明. 但有两个要求:
包中的类所在文件都在同一目录下.属于包的源文件要放在包名对应的目录名中.
这样编译以后的输出文件也在对应的目录.以便用 import包含的时候能找到它们.
package语句必须是文件中的第一个语句.(该句之前只能有空白或注释).
如: package Abc;
引入包中某个类.
如: improt Abc.Xyz;
引入整个包.(之后可以使用该包中的所有的类)
如 improt Abc.*;
不通过引入而直接使用类
null包和放在同一物理目录中的类可以不经过引用而使用. 所以这时可以直接使用.
如果类在包中可以用 包名.类名 的形式使用类.
标准的Java包有:
java.applet 用于创建小应用程序
java.awt 用于编写平台无关的图形用户界面应用程序
java.io 用于输入输出处理
java.lang 一些基本Java类. 这个包中的类是被隐式引入的.用户可以不引入而直接使用其中的类.
java.net 用于建立网络连接
java.util 一些工具和数据结构
5.3 Class类 - core java
Class 类的实例表示正在运行的 Java 应用程序中的类和接口.
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象.
基本类型(如int)和关键字 void 也表示为 Class 对象.
Class类没有公共构造方法. Class 对象是在加载类时由 Java 虚拟机自动构造的.
可以通过 对象.getClass() 或 类.class 得到一个对象或类的Class类.如:
Class c = void.class //void也可以
Class c = int.class
String s = new String("abc"); Class c = s.getClass()
也可以用一个类名字符串得到一个类的Class对象. 使用Class类的静态方法:
public static Class<?> forName(String className) throws ClassNotFoundException
如: Class c = Class.forName("java.lang.Thread")
Class类的方法:
public String toString() //转换为字符串。字符串的表示形式为字符串 "class" 或"interface"
//后面紧跟一个空格,然后是该类的完全限定名
public static Class<?> forName(String className) throws ClassNotFoundException
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader) throws ClassNotFoundException
//取的指定类名className对应的Class对象. 其中参数:
name - 所需类的完全限定名
initialize - 是否必须初始化类
loader - 用于加载类的类加载器
public T newInstance() //创建此 Class 对象所表示的类的一个新实例
public boolean isInstance(Object obj) //判定指定的 Object 是否与此 Class 所表示的对象
//赋值兼容。此方法是 Java 语言 instanceof 运算符的动态等效方法
public boolean isAssignableFrom(Class<?> cls) //判定此 Class 对象所表示的类或接口与指
//定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
public boolean isInterface() //判定指定的 Class 对象是否表示一个接口
public boolean isArray() //是否表示一个数组类
public boolean isPrimitive() //是否表示一个基本类型(8个基本类型和void)
public boolean isAnnotation() //是否表示一个注释类型
public boolean isSynthetic() //是否表示一个复合类
public String getName() //返回此 Class 对象所表示的实体的名称
//若表示的是基本类型或void. 则返回"int"之类. 若是类或接口.则返回"java.lang.String"之类.
//若表示数组类型. 则返回几个"[" + "编码". [的个数是数组嵌套层数. 编码是:
//boolean(Z) byte(B) char(C) double(D) float(F) int(I) long(J) short(S)
//类或接口(L类名)
public ClassLoader getClassLoader() //返回该类的类加载器。基本类型返回null
public Class<? super T> getSuperclass() //返回表示的实体的超类的Class对象.
//对于没有超类的实体(Object类.接口.基本类型.void)该方法返回null.
//对于数组. 返回Object.class
public Type getGenericSuperclass() //此对象所表示的类的超类. 对没有超类的返回null.
//对数组返回Object.class
public Package getPackage() //获取此类的包
public Class[] getInterfaces() //返回它表示的class或interface实现了的接口的Class数组
public Type[] getGenericInterfaces() //返回它表示的class或interface实现了的接口数组
public Class<?> getComponentType() //返回表示数组的组件类型的 Class
public int getModifiers() //返回此类或接口以整数编码的 Java 语言修饰符(如public.final等)
public Object[] getSigners() //获取此类的标记
public Method getEnclosingMethod() //若表示的是某方法中的本地类或匿名类.返回该Method对象.
public Constructor<?> getEnclosingConstructor()
//若表示构造方法中的一个本地或匿名类,则返回 Constructor 对象
public Class<?> getDeclaringClass()//若表示的类或接口是另一个类的成员,则返回该类的Class
public Class<?> getEnclosingClass() //返回基础类的立即封闭类
public String getSimpleName() //返回源代码中给出的基础类的简称
public String getCanonicalName()
//返回《Java Language Specification》中所定义的基础类的规范化名称
public boolean isAnonymousClass() //是否表示一个匿名类
public boolean isLocalClass() //是否表示一个本地类
public boolean isMemberClass() //是否表示一个成员类
public boolean isEnum() //是否表示一个枚举
public T[] getEnumConstants() //若表示枚举. 则返回其元素
public T cast(Object obj) //将obj强制转换成此 Class 对象所表示的类或接口
public <U> Class<? extends U> asSubclass(Class<U> clazz)
//强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类public Class[].
getClasses() ///
public Field[] getFields() //返回表示public字段的 Field 对象的数组
public Method[] getMethods() //返回表示public方法的 Method 对象的数组
public Constructor[] getConstructors() //返回表示public构造方法的Constructor对象的数组
public Field getField(String name) //根据指定的字段名返回一个表示public字段的Field对象.
public Method getMethod(String name, Class... parameterTypes)
//根据名字和参数返回Method
public Constructor<T> getConstructor(Class... parameterTypes) //返回Constructor
//未完. 太多了
5.4 反射 -core java
能够分析类的能力的程序称为反射器. Java提供此功能的包是 java.lang.reflect包.
该包中的三个类Field Method Constructor 分别表示字段.方法和构造器.
这三个类都有getName方法来返回表示的实体的名字. 还有返回类型和返回参数类型等方法.
所以使用反射可以:
运行时分析类的能力
运行时探查对象.比如只写一个toString方法供所有的类使用
实现通用数组操作代码
使用Method对象.它类似于c++中的函数指针
使用反射器分析一个类的例子:
improt java.lang.reflect.*;
import javax.swing.*;
public class ReflectionTest {
public static void main(String[] args) {
string name;
if (args.length > 0) name = args[0];
else name = JOptionPane.showInputDialog("Class name: ");
try {
Class cl = Class.forName(name); //通过类名得到Class对象
Class supercl = cl.getSuperclass(); //取父类的Class对象
System.out.print("class " + name);
if (supercl != null && supercl != Object.class)
System.out.print(" extends " + supercl.getName());
System.out.print("/n{/n"};
printConstructors(cl);
System.out.println();
PrintMethods(cl);
System.out.println();
PrintFields(cl);
System.out.println();
} catch (ClassNotFoundExcption e) { e.printStackTrace(); }
System.exit(0);
}
public static void printConstructors(Class cl) {
Constructor[] constructors = cl.getDeclaredConstructors(); //取构造方法数组
for (int i=0; i<constructors.length; i++) {
Constructor c = constructors[i];
System.out.print(Modifier.toString(c.getModifiers()));
System.out.print(" " + name + "(" );
Class[] paramTypes = c.GetParameterTypes(); //取构造方法参数类型的数组
for (int j=0; j<paramTypes.length; j++) {
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println();
}
}
public static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods(); //取其它方法的数组
for (int i=0; i<methods.length; i++) {
Method m = mothods[i];
Clsss retTyeps = m.getReturnType(); //方法的返回类型的Class
String name = m.getName();
System.out.print(Modifier.toString(m.getModifiers()));
System.out.print(" " + retType.getName() + " " + name + "(" );
Class[] paramTypes = m.getParameterTypes(); //方法的参数类型的数组
for (int j=0; j<paramTypes.length; j++) {
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
Ststem.out.println(");"};
}
}
public static void printFields(Class cl) {
Field[] fields = cl.getDeclaredFields();
for (int i=0; i<fields.length; i++) {
Field f = fields[i];
Class type = f.getType();
String name = f.getName();
System.out.print(Modefier.toString(f.getModifiers()));
Ststem.out.print(" " + type.getName() + " " + name + ";" );
}
}
}
通过Field(表示字段). 可以查看某对象的该字段的值. 使用如下方法:
Object get(Object obj) //对于类类型的字段
int getInt(Object obj) //对于内置类型的字段用类似这样的方法.若用get()则返回的是包装后的对象
如:
class A { public String x; }
A a = new A;
Class c = a.getClass();
Field f = c.getDeclaredField("x"); //取得x字段的Field对象.
Object o = f.get(a); //这样就得到了a.x的值.
这种用法很像c++中通过一个指向数据成员的指针和一个对象来的到该对象对应的成员的值.
对应的. 有get()也有set()来设置obj对象的某个字段:
void set(Object obj, Object value)
void setInt(Object obj, int value) //对于内置类型的字段用这个方法
用Method(表示方法). 可以执行某对象的该方法. 这要使用Method类的方法:
Object invoke(Object obj, Object... args)
这类型于c++中的指向成员函数的指针. 给它帮定了一个对象和函数参数. (如在Loki库中的Functor)
如果Method对象表示的是静态方法. 则它就相当于一个c函数指针. 调用invoke()时第一个参数传null.
6.接口和内部类 - core java
6.1 接口
接口用来避免c++的多继承. 它相当于c++的纯粹抽象类. 它的成员只能是抽象方法和静态常量.
定义接口时如下:
public(或缺省) interface 接口名 extends 接口列表 { }
各个部分和定义class时的意义一样.
接口中声明的变量都是 public final static的.
使用接口
如: class A implements B{ ... }
如果接口中的某个方法带有异常列表.在派生类中覆盖它时方法头中可以不带异常列表而在方法体中直接抛出
异常. 如果覆盖时方法头中带有异常列表则列表中的异常类型只能是接口中列出的异常类型的子集. 实际上
不管覆盖时写的异常列表是什么.在方法中总是可以抛出接口的方法声明时指定的异常. 所以覆盖时通常就不
再带那个异常列表了.
instanceof 关键字
用来检查对象是否属于某个类或接口.
6.2 clone方法
要得到一个对象的深拷贝. 要调用clone()方法. 如:
A a = new A;
A b = a.clone();
但clone方法并不这么简单. 它是Object类的一个protected方法. 所以在A类和A的派生类以外调用这个
方法是不行的. 而且在Object的clone方法的实现中只是对所有的字段进行浅拷贝. 如果A类某个字段是类
类型的. 则这样的浅拷贝可能不是我们希望的结果.
所以要能正确的调用A的clone方法必须:
让A类实现 Cloneable接口. 并用public访问修饰符重新定义clone方法.
但Cloneable接口实际上并没有声明clone方法. 这个接口只是作为一个标记.证明一个类可以调用clone().
如果对一个没有实现Cloneable接口的类调用clone方法.会抛出CloneNotSupportedExption异常.
定义自己的clone方法时如下:
class A extends B implents Cloneable {
C c;
int x;
public Object clone() {
try {
A cloned = (A) super.clone();
cloned.c = (C) c.clone();
return cloned;
} catch (CloneNotSuportedException e) { return null; }
}
}
6.3 内部类
先看一下c++的嵌套类
class LinkList {
public:
class Iterator { ... };
private:
class Link { ... };
};
这样可以通过 LinkList::Iterator 来访问内部的Iterator类. 这样做可以避免名字冲突.
而私有的 LinkList::Link 类在LinkList类外部则是不可访问的. 把内部的Link类的数据成员弄成
public的也是安全的. 因为只有在LinkList类内才能访问它.
Java的内部类是指定义在其它类内部的类.它和c++的嵌套类不同.
Java的内部类的对象持有一个隐式引用.指向那个实例化它的外部类的对象. 通过这个引用我们可以访问外部
对象的全部状态(包括私有). 只有当内部类是static的时内部类的对象没有这种隐式引用. 这时它就和c++
的嵌套类是相似的.
只有内部类可以是 private 访问修饰符的.而外部类只能是public或默认的. 如:
class A {
private class B { .. } //private使得内部类B只能在A内访问.
}
再看看内部类对象如何持有一个外部类对象的指针:
class A {
private int x = 3;
void funa {
B b = new B; // 构造内部类B的对象.
// 实际上编译器隐式的在B的构造器中加上了一个隐藏的参数 A 如: public B(A out)
// 并在构造b时实际上这里编译器是调用了 new B(this) .这样使b就持有了一个指向
// 它的外部类A 的对象的引用. 正是这个引用所指向的A对象创建了b.
// 可以在构造b时显式的指出是哪个外部类对象创建了该内部类对象.而不一定就用this.
// 所以上边的 new B 可以写为 this.new B
b.funb();
}
private class B {
public void funb() { x++; } //此处的 x 为它的外部类A的实例的x字段.
//要取得b所持有的外部类的引用可以用: A.this
//所以上边的x++ 可以写为: A.this.x++
}
}
内部类只是一种语法. 编译之后它和普通类对于虚拟机来说是一样的. 编译内部类的输出文件为:
外部类名$内部类名.class
如上边的内部类B 编译生成的文件为 A$B.class
局部内部类
局部的内部类是指定义在其它类的方法中的类. 如:
class A {
public void fun() {
class B { ... } //一个局部的内部类
}
}
局部内部类不需要public private等修饰.因为它只能在该方法内被使用.即使是A的其它方法也不能使用B.
局部内部类是一种特殊的内部类. 所以它的对象也持有外部类的引用. 也可以访问外部类的成员.
而且局部内部类还可以访问定义在该方法中的final局部变量. 例如:
public void fun(final double d) {
class B {
public void f() { double x = d; } //使用fun的局部final变量
}
B b = new B;
//..........
}
匿名内部类
匿名内部类是一种特殊的局部内部类. 因为有时我们只要为局部类创建唯一的一个对象. 这时就省略了类名:
public void fun() {
ActionListener a = new ActionListener() { //定义匿名类的同时创建匿名类的实例.
//在这里ActionListener是该匿名类的超类(或接口)
public void actionPerformed(ActionEvent e) { ... }
}
addActionListener(a);
}
静态内部类
静态内部类是在定义内部类的时候使用了static关键字. 这样内部类的对象就不再持有外部类对象的引用了.
如果内部类不需要访问外部类的成员就应该定义成静态内部类. 这就和c++中的嵌套类一样了. 这样可以避免
名字冲突.