一、反射
1.反射的介绍
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
java反射机制,可以实现以下功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
- 生成动态代理
2.获取源头Class(重点)
每一个类的.class加载到内存中的对象一个类。
1)获取类的Class对象的三种方式
获取Class对象的方式:
- 1.Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
- 2.类名.class:通过类名的属性class获取
多用于参数的传递
- 3.对象.getClass():getClass()方法在Object类中定义着。
多用于对象的获取字节码的方式
结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
/**
* 反射:
* 获取Class 对象
* String 为例子
* 通过对象获取
* 通过类获取
* 通过字符串获取
*/
public class Reflect001 {
public static void main(String[] args) throws Exception{
//第一种方式:对象.getClass
String str = new String("abc");
Class<?> clz = str.getClass();
System.out.println(clz.getName());
System.out.println(clz);
System.out.println("===============");
//第二种方式:类.class
clz = Person.class;//Person是自定义类,返回的是包名+类名
System.out.println(clz);//class com.shsxt0622.myreflect.Person
System.out.println("==============");
//第三种方式(推荐方式)通过字符串来获取:Class.forName()
String clzName = "java.lang.String";
clz = Class.forName(clzName);
System.out.println(clz);//class java.lang.String
}
}
2)获取类的权限修饰符
获取修饰符,使用getModifiers()即可。
/**
* 获取类修饰符
* 先获取类
* getModifiers()方法
*/
public class Reflect002 {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("world");
Class<?> clz1 = str1.getClass();//第一种方式:对象.getClass
Class<?> clz2 = str2.getClass();
System.out.println(clz1==clz2);//true
int i = clz1.getModifiers();//获取修饰符所对应的int类型的数字
System.out.println(i);//17
System.out.println(Modifier.toString(i));//把i转化为权限修饰符 public final
System.out.println("==============");
}
}
3.创建对象(获取构造器)
1)获取构造器的几种方法
1.getConstructor(...)
获取指定的公开权限的构造器2.getDeclaredConstructor(...)
获取指定的本类的构造器3.getConstructors()
获取所有公开的构造器4.getDeclaredConstructors()
获取本类所有的构造器
2)实例化对象(重点)
创建对象的方式,有new 、克隆、反序列化,再加一种,根据Class对象,使用newInstance() 或者构造器实例化对象。
//无参
Person p = c.newInstance();
//一个参数
p = c.newInstance("张三");
//两个参数
p = c.newInstance("张三",18);
注意:newInstance()是调用空构造,如果空构造不存在,会出现异常。由此可知,使用其他构造器创建对象比较麻烦,使用空构造非常简单。确保空构造存在
3)打开私有权限
//c是构造器,打开构造器的私有权限
c.setAccessible(true);//打开权限
//f是属性,打开属性的私有权限
f.setAccessible(true);
//m是方法打开属性的私有权限
m.setAccessible(true);
综合案例:
public class Reflect003 {
public static void main(String[] args) throws Exception{
//1.获取Class
Class<Person> clz = Person.class;
//2.获取构造器
Constructor<Person> c = clz.getConstructor(null);
System.out.println(c.getName());//com.shsxt0622.myreflect.Person
//3.执行构造器 无参
Person p = c.newInstance();
System.out.println(p);//Person{name='null', age=0}
System.out.println("============");
//一个参数
c = clz.getConstructor(String.class);
System.out.println(c.getName());//com.shsxt0622.myreflect.Person
p = c.newInstance("张三");
System.out.println(p);//Person{name='张三', age=0}
System.out.println("==============");
//两个参数的构造器被private修饰了
c = clz.getDeclaredConstructor(String.class, int.class);//私有的构造器
c.setAccessible(true);//打开私有权限
System.out.println(c.getName());//com.shsxt0622.myreflect.Person
p = c.newInstance("张三",18);
System.out.println(p);//Person{name='张三', age=18}
//1、获取所有的public 权限的构造器
Constructor<?>[] con = clz.getConstructors();
for(Constructor<?> p1 : con){
System.out.println(p1);//public com.shsxt0622.myreflect.Person(java.lang.String)
//public com.shsxt0622.myreflect.Person()
}
}
}
4.获取父类与接口
1.getSuperclass();
获取父类2.getInterfaces();
获取接口
public class Reflect005 {
public static void main(String[] args) throws Exception{
Class<?> clz = Class.forName("com.shsxt0622.myreflect.Son");
//获取父类
Class superClz = clz.getSuperclass();
System.out.println(superClz);//class com.shsxt0622.myreflect.Father
//获取接口
Class<?>[] interfaces = clz.getInterfaces();
System.out.println(interfaces.length);//2
}
}
5.获取属性和方法
1)获取属性
1.getField(属性名);
获取类指定的 public属性成员2. getDeclaredFiled(属性名);
获取类指定的属性成员(包括私有、共有、保护)3.getFields();
获取所有公开的包括本类的父类的属性4.getDeclaredFields();
取得本类的全部属性5.getName();
获取属性对应的名字6.getModifiers();
获取属性对应的权限修饰符
public class Reflect006 {
public static void main(String[] args) throws Exception{
//获取类
Class clz = Class.forName("com.shsxt0622.myreflect.Person");
// 获取属性, 属性对象 私有的 和公开的, 只能是本类的
Field[] fs = clz.getDeclaredFields();
for(Field f : fs){
System.out.println(f);
}
/**
* private java.lang.String com.shsxt0622.myreflect.Person.name
* private int com.shsxt0622.myreflect.Person.age
*/
System.out.println("==================");
// 获取属性 属性对象 所有公开的 包括本类的 父类的
fs = clz.getFields();
for(Field f : fs){
System.out.println(f);
}
System.out.println("==================");
Field f = clz.getField("age");
System.out.println(f);//public int com.shsxt0622.myreflect.Person.age
System.out.println("==================");
Field f1 = clz.getDeclaredField("name");
System.out.println(f1);//private java.lang.String com.shsxt0622.myreflect.Person.name
System.out.println("=====================");
System.out.println(f1.getName());//name
System.out.println(f1.getModifiers());//2 private
System.out.println(f1.getType());//class java.lang.String
}
}
2)获取方法
1.getMethod(方法名, 参数...);
获取指定的公开的方法2. getDeclaredMethod(方法名, 参数...);
获取指定的私有的、公开的方法3.getMethods();
获取所有公开的包括本类的父类的方法4.getDeclaredMethods();
取得本类的全部方法5.getName();
获取方法对应的名字6.getModifiers();
获取方法对应的权限修饰符7.getParameterCount();
获取方法参数的个数8.getReturnType();
获取方法的返回值
关于反射的综合案例:
public class Demo001Reflect {
public static void main(String[] args) throws Exception{
// 创建对象, 不想自己new
Class clz = Class.forName("com.shsxt0623.reflect001.Person");
// 获取构造器
Constructor c = clz.getConstructor();
// 执行构造器
Person p = (Person) c.newInstance();
System.out.println(p);
// 获取私有的构造器
c = clz.getDeclaredConstructor(String.class,int.class);
// 打开权限
c.setAccessible(true);
// 执行构造器
p = (Person) c.newInstance("黄磊",40);
System.out.println(p);
System.out.println("=======================");
// 获取属性
Field field = clz.getField("age");
System.out.println(field.getModifiers());
System.out.println(field.getType());
System.out.println(field.getName());
// 设置指定对象的这个属性的值为 指定的值
field.set(p,35);
System.out.println(p);
System.out.println("================");
// 获取私有属性
field = clz.getDeclaredField("name");
field.setAccessible(true);
field.set(p,"何炅");
System.out.println(p);
//获取指定的公开的方法
Method m = clz.getMethod("testPublic",String.class,String.class);
System.out.println(m);//public void com.shsxt0623.reflect001.Person.testPublic(java.lang.String,java.lang.String)
//非静态方法要传一个对象,后面加参数
m.invoke(p,"张三","李四");//张三----李四
System.out.println("================");
//获取指定的私有的方法
m = clz.getDeclaredMethod("testPrivate", String.class, int.class);
m.setAccessible(true);
System.out.println(m);//private int com.shsxt0623.reflect001.Person.testPrivate(java.lang.String,int)
m.invoke(p,"张三",18);//张三---18
//获取名字
System.out.println(m.getName());//testPrivate
//获取权限修饰符
int i = m.getModifiers();
System.out.println(i);//2
System.out.println(Modifier.toString(i));//private
//获取返回值类型
System.out.println(m.getReturnType());//int
//获取参数的个数
System.out.println(m.getParameterCount());//2
}
}
二、注解
1.注解的介绍
注解相当于是一种嵌入在程序中的元数据,可以使用注解解析工具或编译器对其进行解析,也可以指定注解在编译期或运行期有效。
元数据:描述数据的数据
2.注解的分类
根据注解参数的个数:
1)、标记注解:一个没有成员定义的Annotation类型被称为标记注解。
2)、单值注解:只有一个值
3)、完整注解:拥有多个值
根据注解使用方法和用途:
1)、JDK内置系统注解
2)、元注解
3)、自定义注解
3.内置注解
JavaSE中内置三个标准注解,定义在java.lang中:
1)@Override
限定重写父类方法,若想要重写父类的一个方法时,需要使用该注解告知编译器我们正在重写一个方法。如此一来,当父类的方法被删除或修改了,编译器会提示错误信息;或者该方法不是重写也会提示错误。
Override的源码:
@Target(ElementType.METHOD)表示只能在方法上使用
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
2)@Deprecated
标记已过时,当我们想要让编译器知道一个方法已经被弃用(deprecate)时,应该使用这个注解。Java推荐在javadoc中提供信息,告知用户为什么这个方法被弃用了,以及替代方法是什么
Deprecated的源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
3)@SuppressWarnings
抑制编译器警告,该注解仅仅告知编译器,忽略它们产生了特殊警告。如:在java泛型中使用原始类型。其保持性策略(retention policy)是SOURCE,在编译器中将被丢弃。
SuppressWarnings的源码:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();//value值可以省略
}
4.自定义注解
1)简单入门
@interface:用来声明一个注解。注解类里的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型。可以通过default来声明参数的默认值。
@interface Simple{
//这里定义了一个空的注解,它能干什么呢?我也不知道,但他能用。后面有补充
}
2)元注解
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。Java5.0定义的元注解有四个,这些类型和它们所支持的类在ava.lang.annotation包中可以找到。
1)@Target
用于描述注解的使用范围(即:被描述的注解可以用在什么地方)。表示支持注解的程序元素的种类,一些可能的值有TYPE, METHOD, CONSTRUCTOR, FIELD等等。如果Target元注解不存在,那么该注解就可以使用在任何程序元素之上。取值(ElementType)有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
此时在空注解中加入@Target元注解如:
//此注解只能用在方法上
@Target(ElementType.METHOD)
@interface TestMethod {
}
2)@Retention(了解)
表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)表示注解类型保留时间的长短。取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
此时在上述注解中加入@Retention元注解如:
// 此注解可以用于注解类、接口(包括注解类型) 或enum声明
@Target(ElementType.TYPE)
//该注解运行时有效。注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
@Retention(RetentionPolicy.RUNTIME)
@interface TestRn{
}
3.@Documented(了解)
表示使用该注解的元素应被javadoc或类似工具文档化,它应用于类型声明,类型声明的注解会影响客户端对注解元素的使用。如果一个类型声明添加了Documented注解,那么它的注解会成为被注解元素的公共API的一部分,@Documented是一个标记注解。
//可以被例如javadoc此类的工具文档化
@Documented
@interface TestDoc{
}
4.@Inherited
表示一个注解类型会被自动继承,如果用户在类声明的时候查询注解类型,同时类声明中也没有这个类型的注解,那么注解类型会自动查询该类的父类,这个过程将会不停地重复,直到该类型的注解被找到为止,或是到达类结构的顶层(Object)。
//被子类继承的注解
@Inherited
@interface TestInheri{}
5.自定义注解
定义:
@Target() --> 标识这个注解能用在哪里
@interface 注解名{
返回值类型1 方法名1();
返回值类型2 方法名2() default 对应默认值;
// 返回值类型:基本,String,Class, 枚举, Annotation, 对应数组
}
使用:
关注:Target,注解名,方法(方法名,返回值)
@注解名(方法名1=对应的值,方法名2={值1, 值2})
三、xml
1.概念
可扩展标记语言,Xml用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。
2.特点与作用
1)特点
- xml与操作系统、编程语言的开发平台都无关
- 现不同系统之间的数据交互
2)作用
- 配置应用程序(servlet 配置,框架环境配置等)和网站
- 数据交互
- Ajax基石
3.基础语法
格式良好的 XML 文档需满足以下条件:
- 必须有XML声明语句
- 必须有且仅有一个根元素
- 标签大小写敏感
- 属性值用双引号
- 标签成对
- 元素正确嵌套
注:
- xml声明一般是xml文档的第一行
- xml中,一些字符拥有特殊的意义。如果把字符“<”放在xml元素中,会发生错误,这是因为解析器会把它当作新元素的开始,这样会产生xml错误
xml中5个预定义实体:
1)定义一个合法的xml
<?xml version="1.0" encoding="UTF-8" ?>
<students>
<student>
<id>1</id>
<name>王同</name>
<curriculum>java</curriculum>
<score>89</score>
</student>
<student>
<id>2</id>
<name>李佳</name>
<curriculum>sql</curriculum>
<score>58</score>
</student>
</students>
四、DTD技术
DTD即文档类型定义–Document Type Definition
约束xml中有哪些元素,使每个XML文件可以携带一个自身格式的描述。
一个DTD文档可能包含如下内容:
- 元素的定义规则
- 元素之间的关系规则
- 属性的定义规则
1.使用DTD验证描述XML文档
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE students[
<!ELEMENT students (student*)>
<!ELEMENT student (id,name,curriculum,score)>
<!ELEMENT id (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT curriculum (#PCDATA)>
<!ELEMENT score (#PCDATA)>
]>
<students>
<student>
<id>1</id>
<name>王同</name>
<curriculum>java</curriculum>
<score>89</score>
</student>
<student>
<id>2</id>
<name>李佳</name>
<curriculum>sql</curriculum>
<score>58</score>
</student>
</students>
1)PCDATA
PCDATA 的意思是被解析的字符数据(parsed character data)。
可把字符数据想象为 XML 元素的开始标签与结束标签之间的文本。
PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。
文本中的标签会被当作标记来处理,而实体会被展开。
不过,被解析的字符数据不应当包含任何 &、< 或者 > 字符;需要使用 &、< 以及 > 实体来分别替换它们。
2)CDATA
CDATA 的意思是字符数据(character data)。
CDATA 是不会被解析器解析的文本。在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
<![CDATA[
A>B && A>C
]]>
2.引入DTD的方式
当验证的XML文件较多时,使用内部DTD可能出现冗余,此时可以通过引入dtd 文件进行xml 约束。
1.把DTD约束单独拿出来,新建一个DTD文件
2.在xml中引入
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<!--引入外部的dtd文件来对内容进行约束-->
<!DOCTYPE students SYSTEM "http;//score//scores.dtd"><!--加地址是要双击-->
3.怎么加"http;//score//scores.dtd
"这个地址(idea中)
- 点击idea左上角File–>settings…–>Languages & -->Schemas and DTDs–>点击右上角Project旁边的加号–>在URL里随便写一个地址–>双击dtd文件。
3.Schema技术
Schema是用一套预先规定的xml元素和属性创建的,这些元素和属性定义了xml文档的结构和内容模式。XmlShema规定xml文档实例的结构和每个元素/属性的数据类型。
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.myschema.com.cn"
xmlns="http://www.myschema.com.cn"
elementFormDefault="qualified">
<xs:element name="person">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:integer"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="sex" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<?xml version="1.0" encoding="utf-8" ?>
<person xmlns="http://www.myschema.com.cn">
<id>1</id>
<name>张三</name>
<sex>男</sex>
<address>上海浦东</address>
</person>