java jet_Java基础(二)

1、反射机制

1.1 什么是反射?

反射机制是指在程序运行过程中,对任意一个类都能获取其所有属性和方法,并且对任意一个对象都能调用其任意一个方法。这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

1.2 哪里用到反射机制?

JDBC中,利用反射动态加载了数据库驱动程序。

Web服务器中利用反射调用了Sevlet的服务方法。

Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。

很多框架都用到反射机制,如Spring。最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。

1.3 Java的反射API

Java的反射API主要用于在运行过程中动态生成类,接口或对象等信息。

Class类:用于获取类的属性、方法等信息。

Field类:表示类的成员变量,用于获取和设置类中的属性值。

Method类:表示类的方法,用于执行某个方法或获取方法的描述信息。

Constructor类:表示类的构造方法。

1.4 反射的步骤

获取想要操作的类的Class对象,该Class对象是反射的核心,通过它调用类的任意方法。

调用Class对象所对应的类中定义的方法,这是反射的使用阶段。

使用反射API来获取并调用类的属性和方法。

1.5 获取Class对象的3种方式

1、使用Class类的forName静态方法。如:

Class clazz = Class.forName("java.lang.String");

2、使用类的.class语法。如:

Class clazz = String.class;

3、使用对象的getClass()方法。如:

String str = "aa";

Class clazz= str.getClass();

1.6 创建对象的2种方式

1、使用Class对象的newInstance方法创建该Class对象对应类的实例(这种方法要求Class对象对应的类有默认的空构造器)。

Class clazz = Class.forName("java.lang.String");

Object obj= clazz.newInstance();

2、先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance方法创建Class对象对应类的实例,这种方法可以选定构造方法创建实例。

Class clazz = Class.forName("com.jzq.reflect.Person");

Constructor constructor= clazz.getDeclaredConstructor(String.class,String.class);//获取指定参数构造方法创建对象

Person person = (Person) constructor.newInstance("李四",“男”);

1.7 Method.invoke()动态调用

在运行的代码中通过调用Method的invoke方法实现动态调用,比如动态传入参数及将方法参数化。

代码如下:

(1)定义Person类:

packagecom.jzq.foundation.reflect;public classPerson {privateString name;privateString sex;publicPerson() {

}publicPerson(String name, String sex) {this.name =name;this.sex =sex;

}publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}publicString getSex() {returnsex;

}

@OverridepublicString toString() {return "Person{" +

"name='" + name + '\'' +

", sex='" + sex + '\'' +

'}';

}public voidsetSex(String sex) {this.sex =sex;

}

}

(2)调用invoke方法:

public classMain {public static voidmain(String[] args) {try{//获取Person类的Class对象

Class clazz = Class.forName("com.jzq.foundation.reflect.Person");//获取Class对象中的setName方法

Method method = clazz.getMethod("setName", String.class);

Constructor constructor=clazz.getConstructor();

Object obj=constructor.newInstance();//动态调用obj对象的setName方法并传入王五参数

method.invoke(obj, "王五");

System.out.println(obj.toString());

}catch(ClassNotFoundException e) {

e.printStackTrace();

}catch(NoSuchMethodException e) {

e.printStackTrace();

}catch(IllegalAccessException e) {

e.printStackTrace();

}catch(InstantiationException e) {

e.printStackTrace();

}catch(InvocationTargetException e) {

e.printStackTrace();

}

}

}

结果:

Person{name='王五', sex='null'}

2、注解

注解是一个接口,程序可以通过反射获取指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息。

2.1 作用在代码的注解(定义在java.lang中)

@Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。

@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

@SuppressWarnings - 指示编译器去忽略注解中声明的警告。

2.2 元注解(定义在java.lang.annotation中)

@Target: 标记这个注解应该是哪种 Java 成员。在注解类型的声明中使用target,可以更加明确其修饰的目标。

@Target的取值类型如表:

名称

修饰目标

TYPE

用于描述类、接口(包括注解类型)、枚举、

FIELD

用于描述域

METHOD

用于描述方法

PARAMETER

用于描述参数

CONSTRUCTOR

用于描述构造器

LOCAL_VARIABLE

用于描述局部变量

ANNOTATION_TYPE

用于描述一个注解

PACKAGE

用于描述包

TYPE_PARAMETER

对普通变量的声明

TYPE_USE

能标注任何类型的名称

@Retention:@Retention定义了该注解的被保留级别,即被描述的注解在什么级别有效。有三种类型。

SOURCE:在源文件中有效,即仅在源文件中被保留。

CLASS:在Class文件中有效,即在Class文件中被保留。

RUNTIME:在运行时有效,即在运行时被保留。

@Documented:标记这些注解是否包含在用户文档中。

@Inherited:标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

2.3 Java7后额外的个注解

从 Java 7 开始,额外添加了 3 个注解:

@SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。

@FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。

@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

2.4 注解处理器

1、定义注解接口:

import java.lang.annotation.*;

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@Documentedpublic @interfaceFruitProvider {//供应商编号

public int id() default -1;//供应商名称

public String name() default "";//供应商地址

public String address() default "";

}

2、使用注解接口,定义Apple类,通过注解方式定义一个FruitProvider:

public classApple {

@FruitProvider(id=1,name="西安香蕉厂",address = "陕西省")privateString appleProvider;publicString getAppleProvider() {returnappleProvider;

}public voidsetAppleProvider(String appleProvider) {this.appleProvider =appleProvider;

}

}

3、定义FruitInfoUtil注解处理器,通过反射获取注解数据:

importjava.lang.reflect.Field;public classFruitInfoUtil {public static void getFruitInfo(Class>clazz) {

String strFruitPrivoder;

Field[] fields=clazz.getDeclaredFields();for(Field field : fields) {

FruitProvider fruitProvider= (FruitProvider)field.getAnnotation(FruitProvider.class);

strFruitPrivoder= "供应商编号: "+fruitProvider.id()+"供应商名称: "+fruitProvider.name()+"供应商地址: "+fruitProvider.address();

System.out.println(strFruitPrivoder);

}

}

}

4、调用注解处理器使用:

public classMain {public static voidmain(String[] args) {

FruitInfoUtil.getFruitInfo(Apple.class);

}

}

3、泛型

泛型提供了编译时类型的安全检测机制,该机制允许程序在编译时检测非法的类型。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

注:在不使用泛型的情况下,引用Object类型也可以实现参数任意化,但强制类型转换必须明确知道实际参数的引用类型,不然可能会引起前置类型转换错误,在编译期无法识别这种错误,只能在运行期检测这种错误。

3.1 泛型标记

泛型标记

说明

E(Element)

在集合中使用,表示在集合中存放的元素

T(Type)

表示Java类,包括基础类和自定义类

K(key)

表示键

V(value)

表示值

N(Number)

表示数值类型

?

表示不确定的Java类型

类型通配符使用“?”表示所有具体的参数类型,如:List>在逻辑上是List、List等所有List的父类。

3.2 泛型方法

泛型方法是指方法的参数类型定义为泛型,以便在调用时接收不同类型的参数。在方法的内部根据传递给泛型方法的不同参数类型执行不同的处理方法。

具体用法如下:

public classGeneralClass {public static voidmain(String[] args) {

generalMethod("Hello",3,newWorker());

}public static voidgeneralMethod(T ... inputArray){for(T element:inputArray){if(element instanceofInteger){

System.out.println("处理Integer类型数据");

}else if(element instanceofString){

System.out.println("处理String类型数据");

}else if(element instanceofDouble){

System.out.println("处理Double类型数据");

}else if(element instanceofFloat){

System.out.println("处理Float类型数据");

}else if(element instanceofLong){

System.out.println("处理Long类型数据");

}else if(element instanceofBoolean){

System.out.println("处理Boolean类型数据");

}else if(element instanceofDate){

System.out.println("处理Date类型数据");

}else if(element instanceofWorker){//自定义Worker类

System.out.println("处理Worker类型数据");

}

}

}

}

3.3 泛型类

泛型类是指定义类时在类上定义泛型,以便类在使用时可以根据传入的不同参数类型实例化不同对象。

具体用法如下:

1、定义GeneralClass泛型类:

public class GeneralClass{privateT t;publicT getT() {returnt;

}public voidadd(T t){this.t =t;

}

}

2、使用GeneralClass泛型类:

public classMain {public static voidmain(String[] args) {

GeneralClass generalInt = new GeneralClass();

generalInt.add(1);

System.out.println(generalInt);

GeneralClass generalStr = new GeneralClass();

generalStr.add("jksadhklja");

System.out.println(generalStr);

}

}

结果:

GeneralClass{t=1}

GeneralClass{t=jksadhklja}

3.4 泛型接口

泛型接口的声明与泛型类的声明类似,泛型接口的具体类型一般在实现类中进行声明,不同类型的实现类处理不同的业务逻辑。、

具体代码实现:

1、定义一个泛型接口:

public interface GeneralInterface{publicT getId();

}

2、定义泛型接口的实现类:

public class GeneralInterfaceImpl implements GeneralInterface{publicInteger getId() {

Random random= new Random(1);returnrandom.nextInt();

}

}

3、使用泛型实现类:

public classMain {public static voidmain(String[] args) {

GeneralInterfaceImpl generalInterface= newGeneralInterfaceImpl();

System.out.println(generalInterface.getId());

}

}

3.5 类型檫除

类型檫除:在编码阶段采用泛型时加上的类型参数,会被编译器在编译时去掉。

泛型主要用于编译阶段,在编译后生成的Java字节代码文件中不包含泛型中的类型信息。如,编码时定义的List在经过编译后同一为List。

Java类型檫除过程:

查找用来替换类型参数的具体类(一般为Object)

把代码中的类型参数都替换为具体的类

4、序列化

序列化:将一个对象及其状态信息保存到一个字节数组中。在需要时再将这些字节数组反序列化为对象。

4.1 Java序列化API的使用

Java序列化API为处理对象序列化提供了一个标准机制,应注意以下事项:

类要实现序列化功能,需实现java.io.Serializable接口。

序列化和反序列化必须保持序列化的ID一致,一般使用private static final long serialVersionUID定义序列化ID。

序列化并不保存静态变量。

在需要序列化父类变量时,父类也需要实现Serializable接口。

使用Transient关键字可以阻止该变量被序列化。

具体代码实现如下:

importjava.io.Serializable;public class Worker implementsSerializable {private static final long serialVersionUID = 123456789L;//name将被序列化

privateString name;//transient修饰的变量不会被序列化

private transient intsalary;//静态变量不会被序列化

static int age = 100;publicString getName() {returnname;

}public voidsetName(String name) {this.name =name;

}

}

4.2 序列化与反序列化

序列化框架:fastjson

具体代码如下:

public classMain {public static void main(String[] args) throwsException{//序列化数据到磁盘

FileOutputStream fos = new FileOutputStream("worker.out");

ObjectOutputStream oos= newObjectOutputStream(fos);

Worker testObject= newWorker();

testObject.setName("jzq");

oos.writeObject(testObject);

oos.flush();

oos.close();//反序列化并解析数据状态

FileInputStream fis = new FileInputStream("worker.out");

ObjectInputStream ois= newObjectInputStream(fis);

Worker deTest=(Worker)ois.readObject();

System.out.println(deTest.getName());

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值