Java小结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java小结相关的知识,希望对你有一定的参考价值。

  1. 反射

java数据类型分为原始类型和引用类型。对于每种类型的对象java虚拟机会实例化不可变的java.lang.Class对象,它提供了在运行时检查对象属性的方法,这些属性包括它的成员和类型信息。

注:Class是泛型类,可以使用@SuppressWarnings("unchecked")忽略泛型或者使用Class<?>类型,?表示任意类型。

  1. 获取class对象的五种方法

    1. Object.getClass():如果一个类的对象可用,则最简单的获的Class的方法是使用Object.getClass()(只对引用类型有用);
    2. .class:若果类型可用但没有对象,可以在类型后加上".class"来获的Class对象;
    3. Class.forName():如果知道类的全名,可使用静态方法Class.forName来获的Class对象,但它不能用在原始类型上,抛出ClassNotFoundException异常;
    4. 包装类的Type域:每个原始类型和void都有包装类,利用其Type域就可以获的Class对象(注:Double Integer分别是double以及int的包装类);
    5. 以Class返回值得方法:参考反射API。
  2. getName的返回值

对不同类型的对象或类,class对象的名称是不同的,从这个名称就可以判断原来类型的,所有数组对象都有"["。具体返回值为:

如果此类对象表示的是非数组类型的引用,则返回该类的二进制名称(如java.lang.Class);

如果此类对象表示一个基本类型(如int,long)或void,则返回的名字是一个基本类型或void所对应的java语言关键字相同的字符串;

如果此类对象表示一个数组,名字的内部形式为:表示该数组嵌套深度的一个或多个"["字符加元素类型名,元素类型名的编码为:

元素类型

编码

元素类型

编码

元素类型

编码

boolen

Z

int

I

char

C

byte

B

float

F

long

J

double

D

short

S

class or interface

LclassName

?

?

//1. 获得引用类型名称

String dateName = new Date().getClass().getName();// 对象获的Clas对象

String dateClassName = Date.class.getName();//类获的Class对象

String dateClassForName = Class.forName("java.util.Date").getName();

--他们都返回类的二进制名称:java.util.Date

//2. 获得原始类型名称

String byteName = int.class.getName();// 获得原始类型名称

--返回值为int

//3. 引用类型数组

String oneDimensionArray = new Date[4].getClass().getName();

--一维:[Ljava.util.Date

String twoDimensionArray = new int[4][4].getClass().getName();

--二维:[[I

  1. 查看类声明

通常类的声明包括常见修饰符(public,protected,private,abstract,static,final) 类的名称、类的泛型参数、类的继承类(实现的接口)、类的注解等信息。

Class类的实例表示正在运行的java应用程序中的类和接口。枚举是一个类,注释是一个接口,每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。

技术分享Class类常用方法:

注:Java语言预定义的注解只有@Deprecated可以在运行时获得。

?

Class<?> clazz = Class.forName("java.util.ArrayList");// 获得ArrayList类对象

// 1. 输出类的泛型参数

TypeVariable<?>[] typeVariables = clazz.getTypeParameters();

// 2. 输出类所实现的所有接口

Type[] interfaces = clazz.getGenericInterfaces();

//3. 输出类的直接继承类,如果是继承自Object则返回空

Type superClass = clazz.getGenericSuperclass();

//4. 输出类的所有注释信息,有些注释信息是不能用反射获得的

Annotation[] annotations = clazz.getAnnotations();

一、Type类型

Type是Java编程语言中所有类型的普通的父接口。这些类型包括原生类型(raw types),参数化类型(parameterized types),数组类型(array types),类型变量(type variables)和 原始类型(primitive types)。我们一般不直接操作Type类型,但了解一下Type类型的层次结构还是有必要的。

技术分享1、Type层次结构

?

技术分享2、Class,Method和Field的继承体系

?

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息保存着每个对象所属的类足迹。虚拟机利用运行时信息选择相应的方法执行。然而,可以通过专门的Java类访问这些信息。保存这些信息的类称为Class,泛型形式为Class。Class是反射机制的基础,反射API通过操作Class来获取其完整结构。

  1. 查看类成员

技术分享类的成员包括成员变量、方法、构造器以及内部类等,常用方法如下:

另:getDeclaredClasses() 返回该对象的内部类

classObject.getDeclareField("域的字符串形式")

getDeclareField与getField区别:前者获取所有成员变量,后者只获取public成员变量。

Class<?> clazz = Class.forName("java.util.ArrayList");// 获得ArrayList类对象

//1. 获得该类对象的所有构造方法

Constructor[] constructors = clazz.getConstructors();

//2. 获得该类对象的所有非继承域

Field[] fields = clazz.getDeclaredFields();

//3. 获得该类对象的所有非继承方法

Method[] methods =clazz.getDeclaredMethods();

  1. 按继承层次对类排序

Class类中的isAssignableFrom方法用来判断当前Class对象所表示的类与给定的Class对象所表示的类之间的关系,如果相同或者是其父类,则返回true,如clazz1.isAssignableFrom(clazz2) 若clazz1是否是clazz2父类

排序:

TreeSet<E>是基于TreeMap的NavigableSet实现的,它使用元素的自然顺序或创建TreeSet<E>对象时提供的Comparator对元素进行排序。

//重写Comparator接口指定其泛型类型为Class<?>

public class ClassComparator implements Comparator<Class<?>> {

@Override

// 通过实现Comparator接口来实现比较功能

public int compare(Class<?> clazz1, Class<?> clazz2) {

if (clazz1.equals(clazz2)) {// 如果两个类对象相同则返回0

return 0;

}

if (clazz1.isAssignableFrom(clazz2)) {

return -1; // 如果clazz1所表示的类是clazz2所表示的类的父类则返回-1

}

if (clazz2.isAssignableFrom(clazz1)) {

return 1; // 如果clazz1所表示的类是clazz2所表示的类的子类则返回1

}

throw new IllegalArgumentException("两个类之间没有关系");// 其他情况抛出异常

}

}

//创建TreeSet对象时指定排序方式,以及确定泛型为Class<?>

TreeSet<Class<?>> treeSet = new TreeSet<Class<?>>(new ClassComparator())

treeSet.add(JPanel.class);// 向树集中添加JPanel.class

System.out.println(treeSet.last());// 获得树集的最后一个元素

  1. 动态设置类的私有域

技术分享Field类提供有关类或接口的的单个字段信息,以及对他的动态访问权限,反射的字段可能是一个类(静态)字段或实例字段。Field类常用方法有:

注:对于私有域,首先要用setAccessible()将其可见性设置为true才能设置新值。

在set(Object obj,Object value)函数中,obj为字段所在的对象,若为静态字段,则obj为null,value为设置的值。

?

Student student =new Student(); //创建实例对象

Class<?> stuClass = student.getClass();//获取反射对象

Field accountField = stuClass.getDeclaredField("account");//获取指定字段的值

accountField.setAccessible(true);//字段的可见性设为true

accountField.set(student, 200);//设置值为200

accountField.setDouble(student, 300);

System.out.println(student.getAccount());

  1. 动态调用类中的方法

Java中调用类的方法有两种方式:对于静态方法,可以直接使用类名调用,对于非静态方法,必须使用类的对象调用。

Method类提供类或接口上单独某个方法(以及如何方法该方法)的信息。它通过public Object invoke(Object obj, Object …args)调用方法,obj为方法所在的对象,静态方法为null,…args为参数,返回值为Object类型。

1.获取指定名称的无参数方法

Method showMethod = stuClass.getDeclaredMethod("show");

2.调用指定对象的无参方法

showMethod.invoke(student);

3.获取指定名称的方法,后面的参数是参数的类型

Method showInfo = stuClass.getDeclaredMethod ("showInfo",String.class,int.class);

4.调用指定参数的方法,后面的参数是参数的值,要与参数类型相对应

String rString=(String)showInfo.invoke(student,"DSL",20);

  1. 动态实例化对象

java通常使用构造方法来创建对象,构造方法分为有参数和无参数两种,如果类中没有定义构造方法,编译器会自动添加一个无参数的构造方法。Constructor类提供类的单个构造方法的信息以及对它访问权限。它允许在将实参与带有底层构造方法的形参的newInstrance()匹配时进行转换。

public T newInstance(Object …args)

//获取类的无参数构造器属性

Constructor<Student> constructor = Student.class.getDeclaredConstructor();

//调用无参数的构造器创建对象

Student studentCreate = constructor.newInstance();

//获取类的有参数构造器属性

Constructor<Student> constructorArgs = Student.class.getDeclaredConstructor(String.class,int.class,double.class);

//传入参数创建对象

Student studentCreateArgs = constructorArgs.newInstance("DSL",28,7000);

  1. 创建可变长度数组

ArrayList可以动态添加和删除数组。

Array提供了动态创建和访问java数组的方法,允许在执行get或set操作期间进行扩展转换,它也提供获取/删除指定位置元素的方法。

对于Class类实例,classObject.isArray()判断classObject是否是数组类型。

public static Object increaseArray(Object array) {

Class<?> clazz = array.getClass();// 获得代表数组的Class对象

if (clazz.isArray()) {// 判断其对应的类型是否是数组

// 获得数组元素的类型

Class<?> componentType = clazz.getComponentType();

int length = Array.getLength(array);// 获得输入的数组的长度

// 新建数组指定数组元素类型以及长度,创建新的数组对象

Object newArray = Array.newInstance(componentType, length + 5);

System.arraycopy(array, 0, newArray, 0, length);// 复制原来数组中的所有数据

return newArray;// 返回新建数组

}

return null;// 如果输入的不是数组就返回空

}

  1. 反射与动态代理

使用代理可以在运行时创建一个实现了一组给定接口的新类,这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。

Invocationhandler接口是代理实例的调用处理程序实现的接口,对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke()方法,该方法声明如下:

Object invoke(Object proxy,Method method,Object[] args)

method:所代理的代理类中的方法,由代理类调用其方法时传入

args:代理类方法的参数,由代理类代用其方法时传入

Poxy接口提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。本实例使用该接口中定义的newProxyInstance()方法获的一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。

public static Object newProxyInstance(ClassLoader loader,Class<?> interfaces, InvocationHhandler)

loader:定义代理类的类加载器;

interfances:一个Class对象数组,代理类要实现的接口列表;

InvocationHander:指派方法调用的调用处理程序。

分别通过接口实现以及代理实现接口Seller:

public interface Seller {

void sell();// 简单的测试方法

}

普通方法:

public class HouseSeller implements Seller {

// 实现接口的方法,用输出来区别该类

public void sell() {

System.out.println("销售人员在卖房子"); }

}

?

//在main方法中调用,创建对象,在调用方法

Seller seller = new HouseSeller();

eller.sell();// 普通方式调用sell()方法

?

通过代理实现接口

//1.首先实现InvocationHandler接口,并重写invoke方法,用Proxy创建的代理类实例无论调用什么方法都是在调用Agency中的 invoke()方法

public class Agency implements InvocationHandler {

private Object target;//在构造函数中赋值

/// 用来处理代理类,正真实现代理类的方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("代理人员在卖房子");

//在此处正真调用target中的method方法,要求target实现Seller接口

method.invoke(target,args);

return null;

}

}

?

//在main函数中

// 获得接口Seller类的类加载器

ClassLoader loader = Seller.class.getClassLoader();

//生成代理类实例

seller = (Seller) Proxy.newProxyInstance(loader, new Class[] { Seller.class }, new Agency());

seller.sell();// 代理方式调用sell()方法,其实在调用Agency中的invoke方法

seller.sell(100,"DSL")//有参数的方法

  1. 异常

    1. 异常分类

所有异常都是由Throwable继承而来,在下一层分解为两个分支:Error和Exception。

  • Error类层次结构描述了java运行时系统内部错误和资源耗尽错误(无力解决)。
  • Exception类层次分为两个分支,如下:
  • 程序自身错误导致的异常属于RuntimeException,包括错误的类型转换、数组访问越界、访问空指针、除数为零等。
  • 程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常,如试图在文件尾部后面读取数据、试图打开一个不存在的文件等。

技术分享

未检查异常(unchecked):派生于Error类或RuntimeException类的所有异常称;

已检查(checked):所有其他的异常。

  1. 声明已检查异常

方法上抛出异常:一个方法必须声明所有可能抛出的已检查异常,这样可以从首部反映出这个方法可能抛出哪类已检查异常(如果暂时不处理这些异常)。当重写继承的方法时,抛出的异常不能比超类的范围大,或者不抛出异常。

public Image loadImage(String s) throws FileNotFoundException,EOFException

{

}

方法中抛出异常:对于系统预定义的异常,一般至少有两个构造方法,即空参数构造方法和字符串参数构造方法。使用字符串参数构造方法可以让用户为该构造方法增加提示信息。

public Image loadImage(String s)

{

????throw FileNotFoundException("Hello world")

}

  1. 自定义异常类

自定义异常类派生于Exception或它的子类,包含两个构造函数,可以用getMessage()获取传入的信息:

class FileFormatException extends IOException

{

????pubic FileFormatException()

????{

????}

????public FileFormatException(String str)

????{

????????super(str);

????}

}

  1. 捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈信息。如果调用了一个抛出已检查异常的方法,就必须对它进行处理,或者通过throws/throw将它继续进行传递。

通过try/catch/finally语句块捕获异常。

  1. 再次抛出异常与异常链

在catch子句中可以抛出一个异常,这样做的目的是改变异常的类型,如将SQLException异常转为ServletException异常:

try{…}

catch(SQLException e)

{

????throw new SerleException("database error:"+e.getMesssage());//再次抛出异常

}

将原始异常设置为新异常的"原因":

new SerleException("database error").initCause(e).

重新获取原始异常:Throwable e = se.getCause();

  1. finally子句

不管是否有异常被捕获,finally子句中的代码被执行。

问题:当finally与try同时发生异常时,finally中抛出的异常会覆盖try中抛出的异常。

try{…}

finally

{

????in.close();

}

finally子句包含return语句时。假设利用return语句从try语句块中退出。在方法返回前,finally子句的内容将被执行,如果finally子句中也有一个return语句,这个返回值将会覆盖原始的返回值,如下例,返回值为8

try{

????return 4;

}

finally

{

????return 8;

}

  1. 带资源的try语句

Java SE 7的AutoCloseable接口有一个方法:

void close() throws Exception;

假设资源属于一个实现了AutoCloseable接口的类,可以用带资源的try语句(try-with-resources),它会自动关闭资源:

try(Scanner in = newScanner(new FileInputStream("/usr/share.dict"),

PrintWriter out = new PrintWriter("out.txt"))

{

?

}

不论这个块如何退出,in和out都会关闭。带资源的try语句可以使原来try中的异常重新抛出,而close方法抛出的异常会"被抑制"。

  1. 分析堆栈跟踪元素

堆栈跟踪(stack trace)是一个调用过程的列表,它包含了程序执行过程中方法调用的特定位置。

Throwable类的printStackTrace()方法访问堆栈跟踪的文本信息,显示异常类型、异常信息、异常发生位置;

getMessage ()获取传入异常类的异常信息;

StackTraceElemen类含有能够获得文件名、类名、方法名和当前执行的代码行号;

Throwable t = new Throwable();

StackTraceElement[] frames = t.getStackTrace();

for (StackTraceElement f : frames)

System.out.println(f);

静态的Thread.getAllStackTrace方法,它可以产生所有线程的堆栈跟踪。

以上是关于Java小结的主要内容,如果未能解决你的问题,请参考以下文章

反射学习小结

RTTI和反射小结

Java反射机制

Java小结

Java反射机制详解

Java进阶 六 Java反射机制可恶问题NoSuchFieldException