Java-进阶篇【Junit单元测试、反射、注解、动态代理、XML、XML解析、XPath、设计模式】---10
1:单元测试 概叙 Junit
单元测试:
单元测试就是针对最小的功能单元编写测试代码,Java程序最小的功能单元是方法,因此,单元测试就是针对Java方法的测试,进而检查方法的正确性。
目前测试方法是怎么进行的,存在什么问题
只有一个main方法,如果一个方法的测试失败了,其他方法测试会受到影响。
无法得到测试的结果报告,需要程序员自己去观察测试是否成功。
无法实现自动化测试。
Junit单元测试框架:
JUnit是使用Java语言实现的单元测试框架,它是开源的,Java开发者都应当学习并使用JUnit编写单元测试。
此外,几乎所有的IDE工具都集成了JUnit,这样我们就可以直接在IDE中编写并运行JUnit测试,JUnit目前最新版本是5。
JUnit优点:
JUnit可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
Junit可以生成全部方法的 测试报告。
单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
Junit单元测试是做什么的?
测试类中方法的正确性的。
Junit单元测试的优点是什么?
JUnit可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。
JUnit可以生测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
2:单元测试快速入门 Junit
JUnit单元测试的实现过程是什么样的?
必须导入Junit框架的jar包。
定义的测试方法必须是无参数无返回值,且公开的方法。
测试方法使用@Test注解标记。
JUnit测试某个方法,测试全部方法怎么处理?成功的标志是什么
测试某个方法直接右键该方法启动测试。
测试全部方法,可以选择类或者模块启动。
红色失败,绿色通过。
package com.itheima.d1_junit; import org.junit.*; /** 测试类 【一个业务方法对应一个测试方法】 */ public class TestUserService { // 修饰实例方法的 @Before public void before(){ System.out.println("===before方法执行一次==="); } @After public void after(){ System.out.println("===after方法执行一次==="); } // 修饰静态方法 @BeforeClass public static void beforeClass(){ System.out.println("===beforeClass方法执行一次==="); } @AfterClass public static void afterClass(){ System.out.println("===afterClass方法执行一次==="); } /** 测试方法 注意点: 1、必须是公开的,无参数 无返回值的方法 2、测试方法必须使用@Test注解标记。 */ @Test public void testLoginName(){ UserService userService = new UserService(); String rs = userService.loginName("admin","123456"); // 进行预期结果的正确性测试:断言 // 参数一:失败时候提示 参数二:期望值 参数三:实际值 Assert.assertEquals("您的登录业务可能出现问题", "登录成功", rs ); } @Test public void testSelectNames(){ UserService userService = new UserService(); userService.selectNames(); } }
package com.itheima.d1_junit; /** 业务方法 */ public class UserService { public String loginName(String loginName , String passWord){ if("admin".equals(loginName) && "123456".equals(passWord)){ return "登录成功"; }else { return "用户名或者密码有问题"; } } public void selectNames(){ System.out.println(10/2); System.out.println("查询全部用户名称成功~~"); } }
3:单元测试常用注解
Junit常用注解(Junit 4.xxxx版本) @Test 测试方法 @Before 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 @After 用 来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 @BeforeClass 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 @AfterClass 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 开始执行的方法:初始化资源。 执行完之后的方法:释放资源。 Junit常用注解(Junit 5.xxxx版本) @Test 测试方法 @BeforeEach 用来修饰实例方法,该方法会在每一个测试方法执行之前执行一次。 @AfterEach 用来修饰实例方法,该方法会在每一个测试方法执行之后执行一次。 @BeforeAll 用来静态修饰方法,该方法会在所有测试方法之前只执行一次。 @AfterAll 用来静态修饰方法,该方法会在所有测试方法之后只执行一次。 开始执行的方法:初始化资源。 执行完之后的方法:释放资源。
4:反射 概叙 【拿出类中每个成分】
反射概述: 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。 在运行时,可以直接得到这个类的构造器对象:Constructor 在运行时,可以直接得到这个类的成员变量对象:Field 在运行时,可以直接得到这个类的成员方法对象:Method 这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。 反射的关键: 反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。 HelloWorld.java -> javac -> HelloWorld.class Class c = HelloWorld.class; 反射的基本作用、关键? 反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分。 反射的核心思想和关键就是:得到编译以后的class文件对象。
反射的对象不是new出来的对象,new出来的对象是类的对象,class对象本身,class 文件【类类型,类本身也是一个类型】
【学生类,学生类对象,student.class本身也是一个类类型,本身也是类的对象,类本身也是个对象】
5:反射获取 类 对象
反射的第一步是什么? 获取Class类对象,如此才可以解析类的全部成分 获取Class类的对象的三种方式 方式一:Class c1 = Class.forName(“全类名”); 方式二:Class c2 = 类名.class 方式三:Class c3 = 对象.getClass();
package com.itheima.d2_reflect_class; public class Student { }
package com.itheima.d2_reflect_class; /** 目标:反射的第一步:获取Class对象 */ public class Test { public static void main(String[] args) throws Exception { // 1、Class类中的一个静态方法:forName(全限名:包名 + 类名) 参数为内存中class文件 Class c = Class.forName("com.itheima.d2_reflect_class.Student"); System.out.println(c); // Student.class // 2、类名.class Class c1 = Student.class; System.out.println(c1); // 3、对象.getClass() 获取对象对应类的Class对象。 Student s = new Student(); Class c2 = s.getClass(); System.out.println(c2); } }
// 上面拿的都是一个 class对象,一个class文件编译后只有一个class对象
6:反射 获取 构造器 对象
使用反射技术获取构造器对象并使用 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。 Class类中用于获取构造器的方法 Constructor<?>[] getConstructors() 返回所有构造器对象的数组(只能拿public的) Constructor<?>[] getDeclaredConstructors() 返回所有构造器对象的数组,存在就能拿到 Constructor<T> getConstructor(Class<?>... parameterTypes) 返回单个构造器对象(只能拿public的) Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造器对象,存在就能拿到 使用反射技术获取构造器对象并使用 获取构造器的作用依然是初始化一个对象返回 Constructor类中用于创建对象的方法 T newInstance(Object... initargs) 根据指定的构造器创建对象 public void setAccessible(boolean flag) 设置为true,表示取消访问检查,进行暴力反射
利用反射技术获取构造器对象的方式 getDeclaredConstructors() getDeclaredConstructor (Class<?>... parameterTypes) 反射得到的构造器可以做什么? 依然是创建对象的 public newInstance(Object... initargs) 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象 setAccessible(boolean) 反射可以破坏封装性,私有的也可以执行了。
Constructor:构造器类型
package com.itheima.d3_reflect_constructor; public class Student { private String name; private int age; private Student(){ System.out.println("无参数构造器执行!"); } public Student(String name, int age) { System.out.println("有参数构造器执行!"); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package com.itheima.d3_reflect_constructor; import org.junit.Test; import java.lang.reflect.Constructor; /** 目标:反射_获取Constructor构造器对象. 反射的第一步是先得到Class类对象。(Class文件) 反射中Class类型获取构造器提供了很多的API: 1. Constructor getConstructor(Class... parameterTypes) 根据参数匹配获取某个构造器,只能拿public修饰的构造器,几乎不用! 2. Constructor getDeclaredConstructor(Class... parameterTypes) 根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用! 3. Constructor[] getConstructors() 获取所有的构造器,只能拿public修饰的构造器。几乎不用!!太弱了! 4. Constructor[] getDeclaredConstructors() 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!! 小结: 获取类的全部构造器对象: Constructor[] getDeclaredConstructors() -- 获取所有申明的构造器,只要你写我就能拿到,无所谓权限。建议使用!! 获取类的某个构造器对象:Constructor getDeclaredConstructor(Class... parameterTypes) -- 根据参数匹配获取某个构造器,只要申明就可以定位,不关心权限修饰符,建议使用! */ public class TestStudent01 { // 1. getConstructors: // 获取全部的构造器:只能获取public修饰的构造器。 // Constructor[] getConstructors() @Test public void getConstructors(){ // a.第一步:获取类对象 Class c = Student.class; // b.提取类中的全部的构造器对象(这里只能拿public修饰) 数组 Constructor[] constructors = c.getConstructors(); // c.遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } } // 2.getDeclaredConstructors(): // 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。 @Test public void getDeclaredConstructors(){ // a.第一步:获取类对象 Class c = Student.class; // b.提取类中的全部的构造器对象 Constructor[] constructors = c.getDeclaredConstructors(); // c.遍历构造器 for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "===>" + constructor.getParameterCount()); } } // 3.getConstructor(Class... parameterTypes) // 获取某个构造器:只能拿public修饰的某个构造器 @Test public void getConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器 只能拿public修饰的某个构造器) Constructor cons = c.getConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); } // 4.getConstructor(Class... parameterTypes) // 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。 @Test public void getDeclaredConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器) Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); // c.定位某个有参构造器 Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount()); } }
package com.itheima.d3_reflect_constructor; import org.junit.Test; import java.lang.reflect.Constructor; /** 目标: 反射_获取Constructor构造器然后通过这个构造器初始化对象。 反射获取Class中的构造器对象Constructor作用: 也是初始化并得到类的一个对象返回。 Constructor的API: 1. T newInstance(Object... initargs) 创建对象,注入构造器需要的数据。 2. void setAccessible(true) 修改访问权限,true代表暴力攻破权限,false表示保留不可访问权限(暴力反射) 小结: 可以通过定位类的构造器对象。 如果构造器对象没有访问权限可以通过:void setAccessible(true)打开权限 构造器可以通过T newInstance(Object... initargs)调用自己,传入参数! */ public class TestStudent02 { // 1.调用构造器得到一个类的对象返回。 @Test public void getDeclaredConstructor() throws Exception { // a.第一步:获取类对象 Class c = Student.class; // b.定位单个构造器对象 (按照参数定位无参数构造器) Constructor cons = c.getDeclaredConstructor(); System.out.println(cons.getName() + "===>" + cons.getParameterCount()); // 如果遇到了私有的构造器,可以暴力反射 cons.setAccessible(true); // 权限被打开 【把 类 对象的私有构造器强行打开,让可以创建对象,一次生效,后面这样操作还行需要继续打开】 Student s = (Student) cons.newInstance(); System.out.println(s); System.out.println("-------------------"); // c.定位某个有参构造器 【按照 参数 去匹配定位】 Constructor cons1 = c.getDeclaredConstructor(String.class, int.class); System.out.println(cons1.getName() + "===>" + cons1.getParameterCount()); Student s1 = (Student) cons1.newInstance("孙悟空", 1000); System.out.println(s1); } }
利用反射技术获取构造器对象的方式 getDeclaredConstructors() getDeclaredConstructor (Class<?>... parameterTypes) 反射得到的构造器可以做什么? 依然是创建对象的 public newInstance(Object... initargs) 如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象 setAccessible(boolean) 反射可以破坏封装性,私有的也可以执行了
7:反射 获取 成员变量 对象
使用反射技术获取成员变量对象并使用 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。 Class类中用于获取成员变量的方法 Field[] getFields() 返回所有成员变量对象的数组(只能拿public的) Field[] getDeclaredFields() 返回所有成员变量对象的数组,存在就能拿到 Field getField(String name) 返回单个成员变量对象(只能拿public的) eld getDeclaredField(String name) 返回单个成员变量对象,存在就能拿到 使用反射技术获取成员变量对象并使用 获取成员变量的作用依然是在某个对象中取值、赋值 Field类中用于取值、赋值的方法 void set(Object obj, Object value) 赋值 Object get(Object obj) 获取值。 利用反射技术获取成员变量的方式 获取类中成员变量对象的方法 getDeclaredFields() getDeclaredField (String name) 反射得到成员变量可以做什么? 依然是在某个对象中取值和赋值。 void set(Object obj, Object value): Object get(Object obj) 如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值 setAccessible(boolean)
package com.itheima.d4_reflect_field; public class Student { private String name; private int age; public static String schoolName; public static final String COUNTTRY = "中国"; public Student(){ System.out.println("无参数构造器执行!"); } public Student(String name, int age) { System.out.println("有参数构造器执行!"); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
package com.itheima.d4_reflect_field; import org.junit.Test; import java.io.File; import java.lang.reflect.Field; /** 目标:反射_获取Field成员变量对象。 反射的第一步是先得到Class类对象。 1、Field getField(String name); 根据成员变量名获得对应Field对象,只能获得public修饰 2.Field getDeclaredField(String name); 根据成员变量名获得对应Field对象,只要申明了就可以得到 3.Field[] getFields(); 获得所有的成员变量对应的Field对象,只能获得public的 4.Field[] getDeclaredFields(); 获得所有的成员变量对应的Field对象,只要申明了就可以得到 小结: 获取全部成员变量:getDeclaredFields 获取某个成员变量:getDeclaredField */ public class FieldDemo01 { /** * 1.获取全部的成员变量。 * Field[] getDeclaredFields(); * 获得所有的成员变量对应的Field对象,只要申明了就可以得到 */ @Test public void getDeclaredFields(){ // a.定位Class对象 Class c = Student.class; // b.定位全部成员变量 【返回 成员变量数组】 Field[] fields = c.getDeclaredFields(); // c.遍历一下 for (Field field : fields) { System.out.println(field.getName() + "==>" + field.getType()); } } /** 2.获取某个成员变量对象 Field getDeclaredField(String name); */ @Test public void getDeclaredField() throws Exception { // a.定位Class对象 Class c = Student.class; // b.根据名称定位某个成员变量 Field f = c.getDeclaredField("age"); System.out.println(f.getName() +"===>" + f.getType()); } }
package com.itheima.d4_reflect_field; import org.junit.Test; import java.lang.reflect.Field; /** 目标:反射获取成员变量: 取值和赋值。 Field的方法:给成员变量赋值和取值 void set(Object obj, Object value):给对象注入某个成员变量数据 Object get(Object obj):获取对象的成员变量的值。 void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。 Class getType(); 获取属性的类型,返回Class对象。 String getName(); 获取属性的名称。 */ public class FieldDemo02 { @Test public void setField() throws Exception { // a.反射第一步,获取类对象 Class c = Student.class; // b.提取某个成员变量 Field ageF = c.getDeclaredField("age"); ageF.setAccessible(true); // 暴力打开权限 // c.赋值 【set 指定一个对象负责成员变量】 Student s = new Student(); ageF.set(s , 18); // s.setAge(18); System.out.println(s); // d、取值 int age = (int) ageF.get(s); System.out.println(age); } }
8:反射 获取 方法对象
使用反射技术获取方法对象并使用 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。 Class类中用于获取成员方法的方法 Method[] getMethods() 返回所有成员方法对象的数组(只能拿public的) Method[] getDeclaredMethods() 返回所有成员方法对象的数组,存在就能拿到 Method getMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象(只能拿public的) Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象,存在就能拿到 使用反射技术获取方法对象并使用 获取成员方法的作用依然是在某个对象中进行执行此方法 Method类中用于触发执行的方法 Object invoke(Object obj, Object... args) 运行方法 参数一:用obj对象调用该方法 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写)【没有返回值那么就会返回 null】 利用反射技术获取成员方法对象的方式 获取类中成员方法对象 getDeclaredMethods() getDeclaredMethod (String name, Class<?>... parameterTypes) 反射得到成员方法可以做什么? 依然是在某个对象中触发该方法执行。 Object invoke(Object obj, Object... args) 如果某成员方法是非public的,需要打开权限(暴力反射),然后再触发执行 setAccessible(boolean)
package com.itheima.d5_reflect_method; public class Dog { private String name ; public Dog(){ } public Dog(String name) { this.name = name; } public void run(){ System.out.println("狗跑的贼快~~"); } private void eat(){ System.out.println("狗吃骨头"); } private String eat(String name){ System.out.println("狗吃" + name); return "吃的很开心!"; } public static void inAddr(){ System.out.println("在黑马学习Java!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package com.itheima.d5_reflect_method; import org.junit.Test; import java.lang.reflect.Method; /** 目标:反射——获取Method方法对象 反射获取类的Method方法对象: 1、Method getMethod(String name,Class...args); 根据方法名和参数类型获得对应的方法对象,只能获得public的 2、Method getDeclaredMethod(String name,Class...args); 根据方法名和参数类型获得对应的方法对象,包括private的 3、Method[] getMethods(); 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的 4、Method[] getDeclaredMethods(); 获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。 Method的方法执行: Object invoke(Object obj, Object... args) 参数一:触发的是哪个对象的方法执行。 参数二: args:调用方法时传递的实际参数 */ public class MethodDemo01 { /** * 1.获得类中的所有成员方法对象 */ @Test public void getDeclaredMethods(){ // a.获取类对象 Class c = Dog.class; // b.提取全部方法;包括私有的 Method[] methods = c.getDeclaredMethods(); // c.遍历全部方法 for (Method method : methods) { System.out.println(method.getName() +" 返回值类型:" + method.getReturnType() + " 参数个数:" + method.getParameterCount()); } } /** * 2. 获取某个方法对象 */ @Test public void getDeclardMethod() throws Exception { // a.获取类对象 Class c = Dog.class; // b.提取单个方法对象 Method m = c.getDeclaredMethod("eat"); Method m2 = c.getDeclaredMethod("eat", String.class); // 暴力打开权限了 m.setAccessible(true); m2.setAccessible(true); // c.触发方法的执行 Dog d = new Dog(); // 注意:方法如果是没有结果回来的,那么返回的是null. Object result = m.invoke(d); System.out.println(result); Object result2 = m2.invoke(d, "骨头"); System.out.println(result2); } }
9:反射 的作用-绕过编译阶段为集合添加数据
反射的作用-绕过编译阶段为集合添加数据 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素 ArrayList<Integer> list = new ArrayList<>(); list.add(100); // list.add(“黑马"); // 报错 以前先定义集合的时候 可以声明 泛型,里面只能加 整数类型,加字符串就报错 list.add(99); 泛型只是在编译阶段【写代码的时候】可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,其真实类型都是ArrayList了,泛型相当于被擦除了。
代码 运行起来 list 是一个变量,不再是受到类型约束,指向真实的集合类型 ArrayList 不再带泛型
因为 无论 ArrayList.Integer 还是ArrayList.String 在底层 只需要 一个类 ArrayList 来代表,不需要存储两个类浪费内存
反射为何可以给约定了泛型的集合存入其他类型的元素? 编译成Class文件进入运行阶段的时候,泛型会自动擦除。 反射是作用在运行时的技术,此时已经不存在泛型了。
无论 ArrayList<String> 还是 ArrayList<Integer> 的集合 在底层都是 ArrayList,不会再存储泛型类型
反射为何可以给约定了泛型的集合存入其他类型的元素?
编译成Class文件进入运行阶段的时候,泛型会自动擦除。
反射是作用在运行时的技术,此时已经不存在泛型了。
package com.itheima.d6_reflect_genericity; import java.lang.reflect.Method; import java.util.ArrayList; public class ReflectDemo { public static void main(String[] args) throws Exception { // 需求:反射实现泛型擦除后,加入其他类型的元素 ArrayList<String> lists1 = new ArrayList<>(); ArrayList<Integer> lists2 = new ArrayList<>(); System.out.println(lists1.getClass()); // class对象地址 System.out.println(lists2.getClass()); // class对象地址 System.out.println(lists1.getClass() == lists2.getClass()); // ArrayList.class【都是同一个class文件】class对象地址相同 System.out.println("---------------------------"); ArrayList<Integer> lists3 = new ArrayList<>(); lists3.add(23); lists3.add(22); // lists3.add("黑马"); // 无法直接加 Class c = lists3.getClass(); // ArrayList.class ===> public boolean add(E e) 反射拿到 class文件对象 // 定位c类中的add方法 Method add = c.getDeclaredMethod("add", Object.class);
// 现在参数类型还没有被泛型约束,E就是Object类型任意参数类型。外面还没有传参进来,因为只有对象实例化的时候才初始化泛型。
// 原始的class类对象还没有被实例化,这里就知道拿到了原始的class类对象,而不是实例对象 boolean rs = (boolean) add.invoke(lists3, "黑马"); System.out.println(rs); System.out.println(lists3); ArrayList list4 = lists3; // 不使用 反射 这样操作也是可以的 【突破泛型】 list4.add("白马"); list4.add(false); System.out.println(lists3); } }
10:反射的作用-通用框架的底层原理
反射的作用?
可以在运行时得到一个类的全部成分然后操作。
可以破坏封装性。(很突出)
也可以破坏泛型的约束性。(很突出)
更重要的用途是适合:做Java高级框架
基本上主流框架都会基于反射设计一些通用技术功能。
package com.itheima.d7_reflect_framework; public class Student { private String name; private char sex; private int age; private String className; private String hobby; public Student(){ } public Student(String name, char sex, int age, String className, String hobby) { this.name = name; this.sex = sex; this.age = age; this.className = className; this.hobby = hobby; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } }
package com.itheima.d7_reflect_framework; public class Teacher { private String name; private char sex; private double salary; public Teacher(){ } public Teacher(String name, char sex, double salary) { this.name = name; this.sex = sex; this.salary = salary; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
package com.itheima.d7_reflect_framework; import java.io.FileOutputStream; import java.io.PrintStream; import java.lang.reflect.Field; public class MybatisUtil { /** 保存任意类型的对象 * @param obj */ public static void save(Object obj){ try ( PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy-app/src/data.txt", true)); // 打印流管道 ){ // 1、提取这个对象的全部成员变量:只有反射可以解决 Class c = obj.getClass(); // c.getSimpleName()获取当前类名 c.getName获取全限名:包名+类名 ps.println("================" + c.getSimpleName() + "================"); // 2、提取它的全部成员变量 Field[] fields = c.getDeclaredFields(); // 3、获取成员变量的信息 for (Field field : fields) { String name = field.getName(); // 成员变量 字段名 // 提取本成员变量在obj对象中的值(取值) field.setAccessible(true); String value = field.get(obj) + ""; // 成员变量 具体 值 ps.println(name + "=" + value); } } catch (Exception e) { e.printStackTrace(); } } }
package com.itheima.d7_reflect_framework; import java.util.Date; import java.util.Properties; /** 目标:提供一个通用框架,支持保存所有对象的具体信息。 */ public class ReflectDemo { public static void main(String[] args) throws Exception { Student s = new Student(); s.setName("猪八戒"); s.setClassName("西天跑路1班"); s.setAge(1000); s.setHobby("吃,睡"); s.setSex('男'); MybatisUtil.save(s); Teacher t = new Teacher(); t.setName("波仔"); t.setSex('男'); t.setSalary(6000); MybatisUtil.save(t); } }
11:注解 概叙 【标记 后 特殊处理】
注解概述、作用 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
注解的作用
对Java中类、方法、成员变量做标记,然后进行特殊处理。
例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行
12:自定义 注解
自定义注解 --- 格式 自定义注解就是自己做一个注解来使用。 public @interface 注解名称 { public 属性类型 属性名() default 默认值 ; } Java支持的数据类型【属性类型】 基本上都支持
特殊属性 value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!! 但是如果有多个属性, 且多个属性没有默认值,那么value名称是不能省略的。
注解可以标记 类,构造器,方法,成员变量,局部变量,形成都可以标记 都可以
package com.itheima.d8_annotation; /** 目标:学会自定义注解。掌握其定义格式和语法。 */ @MyBook(name="《精通JavaSE》",authors = {"黑马", "dlei"} , price = 199.5) //@Book(value = "/delete") // @Book("/delete") @Book(value = "/delete", price = 23.5) //@Book("/delete") public class AnnotationDemo1 { @MyBook(name="《精通JavaSE2》",authors = {"黑马", "dlei"} , price = 199.5) private AnnotationDemo1(){ } @MyBook(name="《精通JavaSE1》",authors = {"黑马", "dlei"} , price = 199.5) public static void main(String[] args) { @MyBook(name="《精通JavaSE2》",authors = {"黑马", "dlei"} , price = 199.5) int age = 21; } }
package com.itheima.d8_annotation; public @interface MyBook { String name(); // 属性默认 public 公开的 String[] authors(); // 数组。string类型数组 double price(); }
package com.itheima.d8_annotation; public @interface Book { String value(); // 特殊属性 使用value这唯一一个注解的时候 名字可以不写 【不是唯一的时候,其他参数有默认值也可以不写】 double price() ; //double price() default 9.9; }
13:元注解
元注解:注解注解的注解。 元注解常见有两个: @Target: 约束自定义注解只能在哪些地方使用, @Retention:申明注解的生命周期 @Target中可使用的值定义在ElementType枚举类中,常用值如下 TYPE,类,接口 FIELD, 成员变量 METHOD, 成员方法 PARAMETER, 方法参数 CONSTRUCTOR, 构造器 LOCAL_VARIABLE, 局部变量
@Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下 SOURCE: 注解只作用在源码阶段,生成的字节码文件中不存在 CLASS: 注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值. RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用) 元注解是什么? 注解注解的注解 @Target约束自定义注解可以标记的范围。 @Retention用来约束自定义注解的存活范围
注解默认编译阶段就消失了,编译成class文件之后注解就没了,我们 可以使用 Retention 声明这个注解永远活着
package com.itheima.d8_annotation; /** 目标:认识元注解 */ //@MyTest // 只能注解方法和成员变量 public class AnnotationDemo2 { @MyTest private String name; @MyTest public void test(){ } public static void main(String[] args) { } }
package com.itheima.d8_annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.FIELD}) // 元注解【注解下面的注解】 @Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失 public @interface MyTest { }
14:注解 的 解析
注解的解析:注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。 与注解解析相关的接口 Annotation: 注解的顶级接口,注解都是Annotation类型的对象 AnnotatedElement:该接口定义了与注解解析相关的解析方法、
方法: Annotation[] getDeclaredAnnotations() 获得当前对象上使用的所有注解,返回注解数组 T getDeclaredAnnotation(Class<T> annotationClass) 根据注解类型获得对应注解对象 boolean isAnnotationPresent(Class<Annotation> annotationClass) 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false
所有的类成分Class, Method , Field , Constructor,都实现了AnnotatedElement接口他们都拥有解析注解的能力: 解析注解的技巧:注解在哪个成分上,我们就先拿哪个成分对象。 比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解
package com.itheima.d8_annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Bookk { String value(); double price() default 100; String[] author(); }
package com.itheima.d8_annotation; import org.junit.Test; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; /** 目标:完成注解的解析 */ public class AnnotationDemo3 { @Test public void parseClass(){ // a.先得到类对象 Class c = BookStore.class; // b.判断这个类上面是否存在这个注解 if(c.isAnnotationPresent(Bookk.class)){ // Annotation book = c.getDeclaredAnnotaton(Bookk.class) 多态写法,Annotation 是注解的顶级接口,【要拿对象的特有属性,一般不这么写,顶级接口不能拿子类独有东西】
// 还是拿注解的真实类型,强转 如下 Bookk book = (Bookk) c.getDeclaredAnnotation(Bookk.class); System.out.println(book.value()); System.out.println(book.price()); System.out.println(Arrays.toString(book.author())); } } @Test public void parseMethod() throws NoSuchMethodException { // a.先得到类对象 Class c = BookStore.class; Method m = c.getDeclaredMethod("test"); // 反射拿对象方法 // b.判断这个类上面是否存在这个注解 if(m.isAnnotationPresent(Bookk.class)){ //c.直接获取该注解对象 Bookk book = (Bookk) m.getDeclaredAnnotation(Bookk.class); System.out.println(book.value()); System.out.println(book.price()); System.out.println(Arrays.toString(book.author())); } } } @Bookk(value = "《情深深雨濛濛》", price = 99.9, author = {"琼瑶", "dlei"}) class BookStore{ @Bookk(value = "《三少爷的剑》", price = 399.9, author = {"古龙", "熊耀华"}) public void test(){ } }
15:注解的应用场景:junit框架
package com.itheima.d8_annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.FIELD}) // 元注解 @Retention(RetentionPolicy.RUNTIME) // 一直活着,在运行阶段这个注解也不消失 public @interface MyTest { }
package com.itheima.d8_annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class AnnotationDemo4 { public void test1(){ System.out.println("===test1==="); } @MyTest public void test2(){ System.out.println("===test2==="); } @MyTest public void test3(){ System.out.println("===test3==="); } /** 启动菜单:有注解的才被调用。 [模拟注解] */ public static void main(String[] args) throws Exception { AnnotationDemo4 t = new AnnotationDemo4(); Class c = AnnotationDemo4.class; // a.获取类对象 Method[] methods = c.getDeclaredMethods(); // b.提取全部方法 for (Method method : methods) { // c.遍历方法,看是否有MyTest注解,有就跑它 if(method.isAnnotationPresent(MyTest.class)){ method.invoke(t); // 反射 启动 方法 【测试方法:公开,无参数,无返回值,所以这里不需要传参】 } } } }
16:动态代理
动态代理的优点:
非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接本身做代理
可以为被代理对象的所有方法做代理。
可以在不改变方法源码的情况下,实现对方法功能的增强
不仅简化了编程工作、提高了软件系统的可扩展性,同时也提高了开发效率
package com.itheima.d9_proxy; /** 模拟用户业务功能 */ public interface UserService { String login(String loginName , String passWord) ; void selectUsers(); boolean deleteUsers(); void updateUsers(); }
package com.itheima.d9_proxy; public class UserServiceImpl implements UserService{ @Override public String login(String loginName, String passWord) { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } if("admin".equals(loginName) && "1234".equals(passWord)) { return "success"; } return "登录名和密码可能有毛病"; } @Override public void selectUsers() { System.out.println("查询了100个用户数据!"); try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean deleteUsers() { try { System.out.println("删除100个用户数据!"); Thread.sleep(500); return true; } catch (Exception e) { e.printStackTrace(); return false; } } @Override public void updateUsers() { try { System.out.println("修改100个用户数据!"); Thread.sleep(2500); } catch (Exception e) { e.printStackTrace(); } } }
package com.itheima.d9_proxy; public class Test { public static void main(String[] args) { // 1、把业务对象,直接做成一个代理对象返回,代理对象的类型也是 UserService类型 UserService userService = ProxyUtil.getProxy(new UserServiceImpl()); System.out.println(userService.login("admin", "1234")); System.out.println(userService.deleteUsers()); userService.selectUsers(); userService.updateUsers(); // 走代理 } }
package com.itheima.d9_proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 参数一:类加载器,负责加载代理类到内存中使用。 参数二:获取被代理对象实现的全部接口。代理要为全部接口的全部方法进行代理 参数三:代理的核心处理逻辑 */ public class ProxyUtil { /** 生成业务对象的代理对象。 obj.getClass().getClassLoader() 生成一个代理对象,把代理对象拿到内存中使用,加载代理类到内存中作声一个代理对象 obj.getClass() 获取类对象 getClassLoader:获取类加载器,底层基于业务对象产生一个新的类,新的类是一个代理类,代理类加载进来创建一个代理对象 * @param obj * @return */ public static <T> T getProxy(T obj) { // 返回了一个代理对象了 // return (T) 这是使用 T这个泛型 强转 成传进来的类型,传参进来啥类型,返回个啥类型出去 return (T)Proxy.newProxyInstance( obj.getClass().getClassLoader(), // 参数一:类加载器,负责加载代理类到内存中使用。 obj.getClass().getInterfaces(), // 参数二:获取被代理对象实现的全部接口。代理要为全部接口的全部方法进行代理 new InvocationHandler() { // 参数三:代理的核心处理逻辑 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 参数一:代理对象本身。一般不管 // 参数二:正在被代理的方法 // 参数三:被代理方法,应该传入的参数 long startTimer = System .currentTimeMillis(); // 马上触发方法的真正执行。(触发真正的业务功能) Object result = method.invoke(obj, args); long endTimer = System.currentTimeMillis(); System.out.println(method.getName() + "方法耗时:" + (endTimer - startTimer) / 1000.0 + "s"); // 把业务功能方法执行的结果返回给调用者 return result; } }); } }
17:XML 概叙
XML是可扩展标记语言(eXtensible Markup Language)的缩写,它是是一种数据表示格式,可以描述非常复杂的数据结构,常用于传输和存储数据。
<?xml version="1.0" encoding="UTF-8"?> <data> <sender>张三</sender> <receiver>李四</receiver> <src> <addr>北京</addr> <date>2022-11-11 11:11:11</date> </src> <current>武汉</current> <dest>广州</dest> </data>
XML的几个特点和使用场景: 一是纯文本,默认使用UTF-8编码;二是可嵌套; 如果把XML内容存为文件,那么它就是一个XML文件。 XML的使用场景:XML内容经常被当成消息进行网络传输,或者作为配置文件用于存储系统的信息。
XML是什么?
XML的全称为(EXtensible Markup Language),是一种可扩展的标记语言。
它是是一种数据表示格式,可以用于自定义数据格式。
XML的作用是什么?
用于进行存储数据和传输数据
作为软件的配置文件
18:XML 创建,语法 规则
XML的创建:就是创建一个XML类型的文件,要求文件的后缀必须使用xml,如hello_world.xml
XML的语法规则:
XML文件的后缀名为:xml
文档声明必须是第一行
<?xml version="1.0" encoding="UTF-8" ?> version:XML默认的版本号码、该属性是必须存在的 encoding:本XML文件的编码
XML的标签(元素)规则: 标签由一对尖括号和合法标识符组成: <name></name>,必须存在一个根标签,有且只能有一个。 标签必须成对出现,有开始,有结束: <name></name> 特殊的标签可以不成对,但是必须有结束标记,如:<br/> 标签中可以定义属性,属性和标签名空格隔开,属性值必须用引号引起来<student id = “1”></name> 标签需要正确的嵌套
XML的其他组成 XML文件中可以定义注释信息:<!– 注释内容 --> XML文件中可以存在以下特殊字符
XML文件中可以存在CDATA区【数据区,可以写任意内容,不需要按照上面规则转,xml文件编辑器CD补全】: <![CDATA[ …内容… ]]>
XML的组成格式要求是什么样的? 文件后缀必须是xml 文档声明必须是第一行 必须存在一个根标签,有且只能有一个 XML文件中可以定义注释信息:<!– 注释内容 --> 标签必须成对出现,有开始,有结束标签: <name></name> 必须能够正确的嵌套
19:XML 文档约束 之 DTD
问题:由于XML文件可以自定义标签,导致XML文件可以随意定义,程序在解析的时候可能出现问题。
什么是文档约束?
文档约束:是用来限定xml文件中的标签以及属性应该怎么写。——> 以此强制约束程序员必须按照文档约束的规定来编写xml文件
文档约束的分类
DTD
schema
XML的文档约束-DTD的作用和问题?
可以约束XML文件的编写。
不能约束具体的数据类型。
data.dtd <!ELEMENT 书架 (书+)> <!ELEMENT 书 (书名,作者,售价)> <!ELEMENT 书名 (#PCDATA)> <!ELEMENT 作者 (#PCDATA)> <!ELEMENT 售价 (#PCDATA)>
books.xml <?xml version="1.0" encoding="UTF-8"?> <books> <book id="0001" desc="我是第一本书"> <name> JavaWeb开发教程</name> <author> 张孝祥 </author> <sale>100.00元</sale> </book> <book id="0002"> <name>三国演义</name> <author>罗贯中</author> <sale>100.00元</sale> </book> <sql> select * from user where age >= 18 </sql> </books>
20:XML 文档约束 之 schema
文档约束-schema
schema可以约束具体的数据类型,约束能力上更强大。
schema本身也是一个xml文件,本身也受到其他约束文件的要求,所以编写的更加严谨
XML的文档约束-schema的优点?
可以约束XML文件的标签内容格式,以及具体的数据类型。
本身也是xml文件,格式更严谨
data.xsd <?xml version="1.0" encoding="UTF-8" ?> <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.itcast.cn" elementFormDefault="qualified" > <!-- targetNamespace:申明约束文档的地址(命名空间)--> <element name='书架'> <!-- 写子元素 --> <complexType> <!-- maxOccurs='unbounded': 书架下的子元素可以有任意多个!--> <sequence maxOccurs='unbounded'> <element name='书'> <!-- 写子元素 --> <complexType> <sequence> <element name='书名' type='string'/> <element name='作者' type='string'/> <element name='售价' type='double'/> </sequence> </complexType> </element> </sequence> </complexType> </element> </schema>
hello_world_schema.xml <?xml version="1.0" encoding="UTF-8" ?> <书架 xmlns="http://www.itcast.cn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.itcast.cn data.xsd"> <!-- xmlns="http://www.itcast.cn" 基本位置 xsi:schemaLocation="http://www.itcast.cn books02.xsd" 具体的位置 --> <书> <书名>神雕侠侣</书名> <作者>金庸</作者> <售价>399.9</售价> </书> <书> <书名>神雕侠侣</书名> <作者>金庸</作者> <售价>19.5</售价> </书> </书架>
21:XML解析技术
XML的数据的作用是什么,最终需要怎么处理?
存储数据、做配置信息、进行数据传输。
最终需要被程序进行读取,解析里面的信息
sax:一行行解析,适合操作大文件
DOM:整个文件读取出来作为一个对象解析【文档对象解析】
Dom常见的解析工具:
JAXP SUN公司提供的一套XML的解析的API
JDOM JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析、生成、序列化以及多种操作
dom4j 是JDOM的升级品,用来读写XML文件的。具有性能优异、功能强大和极其易使用的特点,它的性能超过sun公司官方的dom 技术,
同时它也是一个开放源代码的软件,Hibernate也用它来读写配置文件
jsoup 功能强大DOM方式的XML解析开发包,尤其对HTML解析更加方便
Dom4j解析XML-得到Document对象
SAXReader类
构造器:
public SAXReader() 创建Dom4J的解析器对象
Document read(String url) 加载XML文件成为Document对象
Document类:
Element getRootElement() 获得根元素对象
Dom4j解析XML的元素、属性、文本 List<Element> elements() 得到当前元素下所有子元素 List<Element> elements(String name) 得到当前元素下指定名字的子元素返回集合 Element element(String name) 得到当前元素下指定名字的子元素,如果有很多名字相同的返回第一个 String getName() 得到元素名字 String attributeValue(String name) 通过属性名直接得到属性值 String elementText(子元素名) 得到指定名称的子元素的文本 String getText() 得到文本 Dom4J的解析思想? 得到文档对象Document,从中获取元素对象和内容。
<?xml version="1.0" encoding="UTF-8"?> <contactList> <contact id="1" vip="true"> <name> 潘金莲 </name> <gender>女</gender> <email>panpan@itcast.cn</email> </contact> <contact id="2" vip="false"> <name>武松</name> <gender>男</gender> <email>wusong@itcast.cn</email> </contact> <contact id="3" vip="false"> <name>武大狼</name> <gender>男</gender> <email>wuda@itcast.cn</email> </contact> <user> </user> </contactList>
package com.itheima.d1_dom4j; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Test; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.List; /** 目标:学会使用dom4j解析XML文件中的数据。 1、导入dom4j框架。 2、准备一个XML文件。 */ public class Dom4JHelloWorldDemo1 { @Test public void parseXMLData() throws Exception { // 1、创建一个Dom4j的解析器对象,代表了整个dom4j框架 SAXReader saxReader = new SAXReader(); // 2、把XML文件加载到内存中成为一个Document文档对象 // Document document = saxReader.read(new File("xml-app\\src\\Contacts.xml")); // 需要通过模块名去定位 // Document document = saxReader.read(new FileInputStream("xml-app\\src\\Contacts.xml")); // 注意: getResourceAsStream中的/是直接去src下寻找的文件 【返回字节输入流】 模块名称修改只会从src中找 InputStream is = Dom4JHelloWorldDemo1.class.getResourceAsStream("/Contacts.xml"); Document document = saxReader.read(is); // 3、获取根元素对象 Element root = document.getRootElement(); System.out.println(root.getName()); // 4、拿根元素下的全部子元素对象(一级) // List<Element> sonEles = root.elements(); List<Element> sonEles = root.elements("contact"); for (Element sonEle : sonEles) { System.out.println(sonEle.getName()); } // 拿某个子元素 Element userEle = root.element("user"); System.out.println(userEle.getName()); // 默认提取第一个子元素对象 (Java语言。) Element contact = root.element("contact"); // 获取子元素文本 System.out.println(contact.elementText("name")); // 去掉前后空格 System.out.println(contact.elementTextTrim("name")); // 获取当前元素下的子元素对象 Element email = contact.element("email"); System.out.println(email.getText()); // 去掉前后空格 System.out.println(email.getTextTrim()); // 根据元素获取属性值 Attribute idAttr = contact.attribute("id"); System.out.println(idAttr.getName() + "-->" + idAttr.getValue()); // 直接提取属性值 System.out.println(contact.attributeValue("id")); System.out.println(contact.attributeValue("vip")); } }
24:Dom4J解析XML文件-案例实战
Dom4J的解析后的数据形式。
通常数据会封装成Java的对象,如单个对象,或者集合对象形式
<?xml version="1.0" encoding="UTF-8"?> <contactList> <contact id="1" vip="true"> <name> 潘金莲 </name> <gender>女</gender> <email>panpan@itcast.cn</email> </contact> <contact id="2" vip="false"> <name>武松</name> <gender>男</gender> <email>wusong@itcast.cn</email> </contact> <contact id="3" vip="false"> <name>武大狼</name> <gender>男</gender> <email>wuda@itcast.cn</email> </contact> <user> </user> </contactList>
package com.itheima.d1_dom4j; /** <contact id="1" vip="true"> <name> 潘金莲 </name> <gender>女</gender> <email>panpan@itcast.cn</email> </contact> */ public class Contact { private String name; private int id; private boolean vip; private char gender; private String email; public Contact() { } public Contact(String name, int id, boolean vip, char gendar, String email) { this.name = name; this.id = id; this.vip = vip; this.gender = gendar; this.email = email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public boolean isVip() { return vip; } public void setVip(boolean vip) { this.vip = vip; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "Contact{" + "name='" + name + '\'' + ", id=" + id + ", vip=" + vip + ", gendar=" + gender + ", email='" + email + '\'' + '}'; } }
package com.itheima.d1_dom4j; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.junit.Test; import java.util.ArrayList; import java.util.List; public class Dom4JTest2 { @Test public void parseToList() throws Exception { // 需求:解析XML中的数据成为一个List集合对象。 // 1、导入框架(做过) // 2、创建SaxReader对象 SAXReader saxReader = new SAXReader(); // 3、加载XML文件成为文档对象Document对象。 Document document = saxReader.read(Dom4JTest2.class.getResourceAsStream("/Contacts.xml")); // 4、先拿根元素 Element root = document.getRootElement(); // 5、提取contact子元素 List<Element> contactEles = root.elements("contact"); // 6、准备一个ArrayList集合封装联系人信息 List<Contact> contacts = new ArrayList<>(); // 7、遍历Contact子元素 for (Element contactEle : contactEles) { // 8、每个子元素都是一个联系人对象 Contact contact = new Contact(); contact.setId(Integer.valueOf(contactEle.attributeValue("id"))); contact.setVip(Boolean.valueOf(contactEle.attributeValue("vip"))); contact.setName(contactEle.elementTextTrim("name")); contact.setGender(contactEle.elementTextTrim("gender").charAt(0)); contact.setEmail(contactEle.elementText("email")); // 9、把联系人对象数据加入到List集合 contacts.add(contact); } // 10、遍历List集合 for (Contact contact : contacts) { System.out.println(contact); } } }
24:XML检索技术:Xpath
XPath介绍: XPath在解析XML文档方面提供了一独树一帜的路径思想,更加优雅,高效 XPath使用路径表达式来定位XML文档中的元素节点或属性节点。 示例: /元素/子元素/孙元素 //子元素//孙元素
Xpath的四大检索方案
绝对路径
相对路径
全文检索
属性查找
XPath:绝对路径 采用绝对路径获取从根节点开始逐层的查找/contactList/contact/name节点列表并打印信息 /根元素/子元素/孙元素 从根元素开始,一级一级向下查找,不能跨级 XPath:相对路径 先得到根节点contactList 再采用相对路径获取下一级contact 节点的name子节点并打印信息 ./子元素/孙元素 从当前元素开始,一级一级向下查找,不能跨级 XPath:全文搜索 直接全文搜索所有的name元素并打印 //contact 找contact元素,无论元素在哪里 //contact/name 找contact,无论在哪一级,但name一定是contact的子节点 //contact//name contact无论在哪一种,name只要是contact的子孙元素都可以找到 XPath:属性查找 在全文中搜索属性,或者带属性的元素 //@属性名 查找元素对象,全文搜索指定元素名和属性名。 //元素[@属性名] 查找元素对象,全文搜索指定元素名和属性名,并且属性值相等。 //元素//[@属性名=‘值’] 查找元素对象,全文搜索指定元素名和属性名,并且属性值相等。 Xpath作用,四大类。 检索XML文件中的信息 绝对路径: /根元素/子元素/孙元素 相对路径:./子元素/孙元素 全文检索://contact 属性查找://@属性名 、//元素[@属性名]、//元素//[@属性名=‘值’]
<?xml version="1.0" encoding="UTF-8"?> <contactList> <contact id="1" vip="true"> <name> 潘金莲 </name> <gender>女</gender> <email>panpan@itcast.cn</email> </contact> <contact id="2" vip="false"> <name>武松</name> <gender>男</gender> <email>wusong@itcast.cn</email> </contact> <contact id="3" vip="false"> <name>武大狼</name> <gender>男</gender> <email>wuda@itcast.cn</email> </contact> <user> <contact> <info> <name id="888">我是西门庆</name> </info> </contact> </user> </contactList>
package com.itheima.d2_xpath; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; import org.junit.Test; import java.util.List; /** 目标:XPath检索XML中的信息啊。(了解) 引入: Dom4J可以用于解析整个XML的数据。 但是如果要检索XML中的某些信息,建议使用XPath.(Xpath依赖Dom4j技术) Dom4J用于解析数据,Xpath用于检索数据。 XPath使用步骤: 1.导入dom4j框架。(XPath依赖于Dom4j技术,必须先导入dom4j框架!) 2.导入XPath独有的框架包。jaxen-1.1.2.jar XPath常用API: List<Node> selectNodes(String var1):检索出一批节点集合。 Node selectSingleNode(String var1):检索出一个节点返回。 XPath提供的四种检索数据的写法: 1.绝对路径。 2.相对路径。 3.全文搜索。 4.属性查找。 小结: 1.绝对路径: /根元素/子元素/子元素。 2.相对路径: ./子元素/子元素。 (.代表了当前元素) 3.全文搜索: //元素 在全文找这个元素 //元素1/元素2 在全文找元素1下面的一级元素2 //元素1//元素2 在全文找元素1下面的全部元素2 4.属性查找。 //@属性名称 在全文检索属性对象。 //元素[@属性名称] 在全文检索包含该属性的元素对象。 //元素[@属性名称=值] 在全文检索包含该属性的元素且属性值为该值的元素对象。 */ public class XPathDemo { /** 1.绝对路径: /根元素/子元素/子元素。 */ @Test public void parse01() throws Exception { SAXReader saxReader = new SAXReader(); // a、创建解析器对象 Document document = // b、把XML加载成Document文档对象 saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts2.xml")); List<Node> nameNodes = document.selectNodes("/contactList/contact/name"); // c、检索全部的名称 for (Node nameNode : nameNodes) { Element nameEle = (Element) nameNode; System.out.println(nameEle.getTextTrim()); } } /** 2.相对路径: ./子元素/子元素。 (.代表了当前元素) */ @Test public void parse02() throws Exception { // a、创建解析器对象 SAXReader saxReader = new SAXReader(); // b、把XML加载成Document文档对象 Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts2.xml")); Element root = document.getRootElement(); // c、检索全部的名称 List<Node> nameNodes = root.selectNodes("./contact/name"); for (Node nameNode : nameNodes) { Element nameEle = (Element) nameNode; System.out.println(nameEle.getTextTrim()); } } /** 3.全文搜索: //元素 在全文找这个元素 //元素1/元素2 在全文找元素1下面的一级元素2 //元素1//元素2 在全文找元素1下面的全部元素2 */ @Test public void parse03() throws Exception { // a、创建解析器对象 SAXReader saxReader = new SAXReader(); // b、把XML加载成Document文档对象 Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts2.xml")); // c、检索数据 //List<Node> nameNodes = document.selectNodes("//name"); // List<Node> nameNodes = document.selectNodes("//contact/name"); List<Node> nameNodes = document.selectNodes("//contact//name"); for (Node nameNode : nameNodes) { Element nameEle = (Element) nameNode; System.out.println(nameEle.getTextTrim()); } } /** 4.属性查找。 //@属性名称 在全文检索属性对象。 //元素[@属性名称] 在全文检索包含该属性的元素对象。 //元素[@属性名称=值] 在全文检索包含该属性的元素且属性值为该值的元素对象。 */ @Test public void parse04() throws Exception { // a、创建解析器对象 SAXReader saxReader = new SAXReader(); // b、把XML加载成Document文档对象 Document document = saxReader.read(XPathDemo.class.getResourceAsStream("/Contacts2.xml")); // c、检索数据 List<Node> nodes = document.selectNodes("//@id"); // 当前定位到的不是元素,而是属性对象 for (Node node : nodes) { Attribute attr = (Attribute) node; System.out.println(attr.getName() + "===>" + attr.getValue()); } // 查询name元素(包含id属性的) //@id 这种才是定位属性, //name[@id=888]这个定位的是元素 // Node node = document.selectSingleNode("//name[@id]"); Node node = document.selectSingleNode("//name[@id=888]"); Element ele = (Element) node; System.out.println(ele.getTextTrim()); } }
25:设计模式:工厂模式
什么是工厂设计模式?
之前我们创建类对象时, 都是使用new 对象的形式创建,在很多业务场景下也提供了不直接new的方式 。
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一, 这种类型的设计模式属于创建型模式,它提供了一种获取对象的方式。
工厂设计模式的作用:
工厂的方法可以封装对象的创建细节,比如:为该对象进行加工和数据注入。
可以实现类与类之间的解耦操作(核心思想)
【假设华为对象不好使用,在FactoryPattenrn工厂里修改就行,不需要demo业务代码修改,每个类解耦了,工厂集中维护,对象管理】
工厂设计模式的作用
对象通过工厂的方法创建返回,工厂的方法可以为该对象进行加工和数据注入
可以实现类与类之间的解耦操作(核心思想)
package com.itheima.d3_factory_pattern; public abstract class Computer { private String name; private double price; public abstract void start(); public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
package com.itheima.d3_factory_pattern; public class Mac extends Computer{ @Override public void start() { System.out.println(getName() + "以非常优雅的方法启动了,展示了一个苹果logo"); } }
package com.itheima.d3_factory_pattern; public class Huawei extends Computer{ @Override public void start() { System.out.println(getName() + "开机了,展示了华为的菊花图标~~~~"); } }
package com.itheima.d3_factory_pattern; public class FactoryPattern { /** 定义一个方法,创建对象返回 线程池也是一种工厂模式 */ public static Computer createComputer(String info){ switch (info){ case "huawei": Computer c = new Huawei(); c.setName("huawei pro 16"); c.setPrice(5999); return c; case "mac": Computer c2 = new Mac(); c2.setName("MacBook pro"); c2.setPrice(11999); return c2; default: return null; } } }
package com.itheima.d3_factory_pattern; /** 目标:工厂模式。 什么是工厂设计模式? 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。 这种类型的设计模式属于创建型模式,它提供了一种创建对象的方式。 之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象。 工厂设计模式的作用: 1.对象通过工厂的方法创建返回,工厂的方法可以为该对象进行加工和数据注入。 2.可以实现类与类之间的解耦操作(核心思想,重点) 小结: 工厂模式的思想是提供一个工厂方法返回对象! */ public class FactoryDemo { public static void main(String[] args) { Computer c1 = FactoryPattern.createComputer("huawei"); c1.start(); Computer c2 = FactoryPattern.createComputer("mac"); c2.start(); } }
26:设计模式:装饰模式
什么是装饰设计模式?:创建一个新类,包装原始类,从而在新类中提升原来类的功能
装饰设计模式的作用:
作用:装饰模式指的是在不改变原类的基础上, 动态地扩展一个类的功能。
InputStream(抽象父类)
FileInputStream(实现子类,读写性能较差)
BufferedInputStream(实现子类,装饰类,读写性能高)
定义父类。
定义原始类,继承父类,定义功能。
定义装饰类,继承父类,包装原始类,增强功能!!
装饰设计模式的作用?:装饰模式指的是在不改变原类的基础上, 动态地扩展一个类的功能
package com.itheima.d4_decorator_pattern; /** 共同父类【抽象类】 */ public abstract class InputStream { public abstract int read(); public abstract int read(byte[] buffer); }
package com.itheima.d4_decorator_pattern; import java.util.Arrays; /** 原始类:实现类继承父类 */ public class FileInputStream extends InputStream{ @Override public int read() { System.out.println("低性能的方式读取了一个字节a"); return 97; } @Override public int read(byte[] buffer) { buffer[0] = 97; buffer[1] = 98; buffer[2] = 99; System.out.println("低性能的方式读取了一个字节数组:" + Arrays.toString(buffer)); return 3; } }
package com.itheima.d4_decorator_pattern; /** 装饰类:继承InputStream 拓展原始类的功能【原始类对象拿过来包装】 */ public class BufferedInputStream extends InputStream{ private InputStream is; public BufferedInputStream(InputStream is){ // 构造器接收低级流,包装成高级流【缓存字节输入流包一个字节输入流】 this.is = is; } @Override public int read() { System.out.println("提供8KB的缓冲区,提高读数据性能~~~~"); return is.read(); } @Override public int read(byte[] buffer) { System.out.println("提供8KB的缓冲区,提高读数据性能~~~~"); return is.read(buffer); } }
package com.itheima.d4_decorator_pattern; /** 装饰模式 定义父类:InputStream 定义实现类:FileInputStream 继续父类 定义功能 定义装饰实现类:BufferedInputStream 继承父类 定义功能 包装原始类,增强功能。 */ public class DecoratorPattern { public static void main(String[] args) { InputStream is = new BufferedInputStream(new FileInputStream()); System.out.println(is.read()); System.out.println(is.read(new byte[3])); } }