Junit * 进行单元测试,可以直接运行普通方法(不通过main方法) * 步骤: 1. 创建一个类(类名不要是Test) 2. 创建一个方法,要求:必须是 public;没有返回值void;非静态的;方法名称自定义,无参 3. 在方法上方添加@Test注解 4. 在@Test上按ctrl+1,选择Add Junit 4 library to the build path 5. 选中方法名称,右键run as-->junit test 也可以: 右键-->new-->Junit Test Case,然后根据提示操作 注意:如果不选择方法名称,则所有方法全部运行 示例: import org.junit.Test; public class Student { @Test public void aaa(){ System.out.println("aaa"); } } * @Test 如果想单独运行某个方法,则必须加此注解,否则报initializationError错误(运行全部方法时会忽略此方法) * @Ignore 如果使用此注解进行标记,表示忽略当前方法运行(不管有没有@Test) * @Before 加此注解的方法会在每个方法运行前先运行一遍(不用加@Test) * @After 加此注解的方法会在每个方法运行后再运行一遍(不用加@Test) * Assert类 断言,通过程序进行数据的校验,不正确就报错.此类提供了一些静态方法进行数据的校验 JDK5特性 * Static Import 静态导入 * 在一个类中通过静态导入,可以直接使用另一个类的静态资源(静态字段、静态方法) * 格式:import static 包.类.字段|方法名称|* 例:import static java.util.Collections.sort; 使用时可直接调用,就像本类的静态成员一样 * Autoboxing/Unboxing 自动装箱与拆箱,基本类型与包装类型的转换 * Varargs 可变参数 在定义方式时将参数声明成可变参数,在调用时实际参数的个数不确定。 声明可变参数格式:类型 ... 变量名称 可变参数实质上是数组 如果方法有多个参数,则可变参数只能写在参数列表最后 示例: 声明时: void sum(int... a){ void sum(String s,int... a){ 调用时: //可以传任意多的int参数,或传入一个int数组,但是不能int[]和 其它 同时传 sum(1,2,3); //sum()也可以 sum(new int[]{1,2,3}) 可变参数 和 数组 不能构成重载 当传入的参数不是数组时,将参数装到一个新数组中传递给方法;当传入的参数是一个数组时,直接传递过去 注意Object... 同时传入Object和Object[],则可变参数的长度为2,虚拟机会将Object[]也认为是一个Object类型的,然后装到一个新数组的其中一个元素中 如果传入Object[],但这个Object[]的引用类型为Object,则也会装到新数组的其中一个元素中.所以注意一定要先强转 * Generics 泛型 * Enhanced for Loop 增强for循环 格式: for(数据类型 变量名:数组或者collection集合){ 执行语句; } 增强for循环 可以遍历实现了Iterable接口的类 通常增强for循环只用于遍历目标(修改不方便,增删不能做) 在遍历过程中可以进行修改操作,不能进行增删操作(会抛异常) * Typesafe Enums ,枚举 枚举的意思是一个一个举例 枚举就是多例的简化写法(不让你创建对象,只能使用本来定义的多个对象) 枚举也是类 定义方式: public enum 枚举类名{ 变量名,变量名...; } 枚举成员的修饰词默认为:public static final 注意: 成员变量必须放置在类开始。 枚举的构造方法必须私有 枚举API 枚举有一些默认的方法,可以将 枚举和字符串或整形进行相互转换 对象.name() 枚举-->字符串 对象.ordinal() 枚举-->整形 类名.valueOf(str) 字符串-->枚举 类名.values()[index] 整形-->枚举 values可返回所有枚举的数组,通过索引可获得指定枚举对象 带成员变量和方法的枚举(了解) public enum Gender { male("男"),female("女"); private String name; private Gender(String name){ this.name = name; } public String getName(){ return this.name; } } 调用: Gender m1 = Gender.male; Gender f1 = Gender.female; System.out.println("m1 enum --> " + m1.getName()); System.out.println("f1 enum --> " + f1.getName()); * Annotations 注解 注解就是一个类。用于取代xml配置文件。随着项目越来越庞大,xml配置文件中将存在大量的配置信息,导致程序难以维护与扩展。配置信息仍然存在,将使用注解进行配置 注解本身没有任何作用,相对于一个记号,可以传递值,解析时根据值进行相应的操作 应用: 2.1声明注解(定义) 2.2使用注解 2.3解析注解(获得使用时添加的配置信息) JDK提供三个基本注解 @Override : JDK5.0 表示子类对父类方法的复写。 JDK6.0开始还表示方法是实现接口的方法 @Deprecated : 表示被修饰的方法已经过时,不建议使用(但还可以使用) @SuppressWarnings : 表示被修饰的对象,所确定的内容进行抑制编译器警告 rawtypes , 类型安全警告 unused , 未使用警告 null , 空指针警告 deprecation,使用过期方法的警告 serial,实现序列号接口implements java.io.Serializable,但没有提供序列号警告 all,所有内容警告 建议:需要将所有的警告尽量去解决 声明注解 格式:修饰词 @interface 注解名{内容} //修饰词只能为public 或 abstract,不写默认为public 内容格式:修饰符 类型 名称() [default 默认值]; 例如:public String username(); 类型:String字符串 / Class类对象 / 其他注解 / enum枚举 / 基本类型 , 以及以上类型对应的一维数组 名称 使用时,”名称“称为属性attribute名称。 解析时,”名称“称为方法名称 元注解 用于修饰注解(自定义)的注解(JDK提供)。 @Retention,被修饰的注解,生命周期。 @Retention(RetentionPolicy.SOURCE) ,表示被修饰的注解只存在于源码中。提供给编译器使用 @Retention(RetentionPolicy.CLASS),表示被修饰的注解只存在于源码和字节码文件中。提供给JVM(java虚拟机)使用 @Retention(RetentionPolicy.RUNTIME),表示被修饰的注解存在于源码、字节码文件或内存中。提供程序获得内容,最常使用 @Target,被修饰的注解,修饰的对象。 @Target(ElementType.TYPE) ,被修饰的注解,只能使用在【类、接口】等对象上 @Target(ElementType.CONSTRUCTOR),被修饰的注解,只能使用在【构造方法】对象上 @Target(ElementType.METHOD),被修饰的注解,只能使用在【方法】对象上 @Target(ElementType.FIELD),被修饰的注解,只能使用在【字段】对象上 注意:可以同时赋多个值,放到数组中 @Documented 被修饰的注解,将存在于生成的JavaDoc中(在方法详细信息中可以看到,注意:注解应该在文档注释下方) 使用注解 使用格式:在修饰对象上方添加 @注解类名称(属性名称=值,属性名称={值1,值2}) 注意: 修饰对象参见上面的<声明注解>中的<元注解>中的<@Target> 如果定义类型为数组,使用需要使用花括号 {},多个值使用逗号分割。如果只有一个值,可以不写花括号 如果使用时,属性只有一个,且名称为value,则value=可以省略 解析注解 AnnotatedElement接口(java.lang.reflect) 被修饰的注解必须实现的接口 已知实现类:java.lang.Class / java.lang.reflect.Constructor / java.lang.reflect.Method/ java.lang.reflect.Field 方法 boolean isAnnotationPresent(Class<? extends Annotation> annotationType) 当前对象是否被指定的注解annotationClass修饰 <T extends Annotation> T getAnnotation(Class<T> annotationType) 如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。 Annotation[] getAnnotations() 返回此元素上存在的所有注解。 Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注解。 获取注解的值 obj.方法名称(); 案例:使用注解配置JDBC连接参数 创建注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface JdbcInfo { public String driver(); public String url(); public String user(); public String password(); } 使用和解析注解 public class JdbcUtils { @JdbcInfo(driver = "com.mysql.jdbc.Driver",url = "jdbc:mysql://localhost:3306/ebookstore",user = "root",password = "1234") public static Connection getConnection() throws Exception{ /**解析注解 start*/ // 1 获得方法 Method method = JdbcUtils.class.getMethod("getConnection"); // 2 在当前方法上判断是否有指定的注解 if(! method.isAnnotationPresent(JdbcInfo.class)){ return null; //没有返回null } // 3 获得注解对象 JdbcInfo jdbcInfo = method.getAnnotation(JdbcInfo.class); // 4 取值 String driver = jdbcInfo.driver(); String url = jdbcInfo.url(); String user = jdbcInfo.user(); String password = jdbcInfo.password(); /**解析注解 end*/ //手动的获得连接 // * 注册驱动 Class.forName(driver); // * 获得连接 Connection conn = DriverManager.getConnection(url, user, password); return conn; } } 测试 public class TestDao { public static void main(String[] args) throws Exception { Connection conn = JdbcUtils.getConnection(); System.out.println(conn); conn.close(); } } servlet3.0提供注解用于servlet,过滤器,监听器的配置,还提供注解支持文件上传.参见《Servlet-JSP相关》的<servlet3.0> 问题示例及说明 * 方法的重载问题(类型转换和自动装箱问题) public static void print(int i){ System.out.println("1 int "); } public static void print(double i){ System.out.println("2 double "); } public static void print(Integer i){ System.out.println("3 Integer "); } public static void print(Double i){ System.out.println("4 Double "); } 1. 调用print(100),结果是什么 2. 注释掉第一个方法,调用print(100),结果是什么 3. 注释掉前两个方法,调用print(100),结果是什么 4. 注释掉前三个方法,调用print(100),结果是什么 答案: 1. 输出1 int,类型匹配 2. 输出2 double,基本类型 自动 向上转型 3. 输出3 Integer,装箱 4. 报错,装箱 Integer 不能 转成 Double * 反射调用参数为数组的方法的问题(可变参数问题) 如:反射调用main方法 Method method = clazz.getMethod("main", String[].class);//clazz为某个类的字节码对象 String[] arr = {"1"}; method.invoke(null, arr); 为什么报错? invoke第二个参数为可变参数形式,接收值为Object 猜想: invoke在内部遍历可变数组,然后把其中的每个元素传递给方法的形参 所以,要调用main方法则: 传递给invoke的第二个参数必须是一个数组,而数组的第一个元素是一个String[]类型 也可以先将String[]类型强转为Object类型,则可变参数自动将此String[]类型放置到一个新数组的第一个元素. * 关于this的问题 public class parent{ public void init(){ System.out.println(1); this.demo(); } public void demo(){ System.out.println(2); } } public class Son extends Parent{ public void init(){ super.init(); System.out.println(3); this.demo(); } public void demo(){ System.out.println(4); } public static void main(String[] args){ Son son = new Son(); son.init(); } } 输出结果: 1 4 3 4 就算是super调用了父类方法,但父类方法运行时的this仍是子类对象 类加载器 ClassLoader java.lang.ClassLoader 类加载器是负责加载类的对象。将class文件转换成Class对象 类加载器获得方式:类.class.getClassLoader() 加载器结构 StrapClassLoader,引导类加载器 加载内容:rt.jar --> runtime运行时必须资源。(包含内容:java.lang | java.util | java.sql 等) 对象:null --> 不是类 ExtClassLoader,扩展类加载器 加载内容:jre\lib\ext 所有内容 对象:sun.misc.Launcher$ExtClassLoader AppClassLoader ,应用类加载器 加载内容:\WEB-INF\classes 所有内容(源码对应字节码) 、\WEB-INF\lib 使用第三方jar 对象:sun.misc.Launcher$AppClassLoader 可以通过System.getProperty()获得加载的内容,key分别为sun.boot.class.path, java.ext.dirs, java.class.path 我们的程序由应用类加载器加载,应用类加载器由扩展类加载器加载,扩展类加载器由引导类加载器加载,引导类加载器不是类,是由c语言编写的 classloader 加载类用的是全盘负责委托机制 全盘负责:如果一个类A不在内存,当前类B如果要使用类A,必须使用当前类B的类加载器,去加载类A 委托机制:一个类加载器如果要加载资源,必须先询问父加载器是否加载资源,如果已经加载则直接使用。否则在加载。 将保证每一个类在内存中只会被加载一次。 动态代理 Proxy 动态代理:在程序运行时,动态的创建一个代理类,对目标类进行代理,从而对目标方法进行增强。 静态代理:编写方法(装饰者) -- 创建一个类 JDK 提供java.lang.reflect.Proxy工具类,用于创建动态代理类,前提:必须有接口 Object obj = Proxy.newProxyInstance(ClassLoader loader ,Class[] interfaces) 返回值obj就是代理类对象,可以强转为接口类型,当做接口的实现类使用 参数1:loader ,使用指定类加载器加载代理类对象. 通常为 当前类名.class.getClassLoader() 参数2:interfaces , 要代理的接口,会根据接口创建一个空的代理类. 通常为 原接口实现类对象.getClass().getInterfaces() 参数3:InvocationHandler ,InvocationHandler接口的实现类,要重写接口中的invoke方法,指定当使用代理类调用方法时的操作 需要重写invoke方法 Object invoke(Object proxy, Method method, Object[] args) 参数1 proxy : 代理对象 参数2 method :当前执行的方法 参数3 args : 当前执行方法实际参数。 返回值会作为调用接口方法的返回值 获得方法名称:method.getName() 执行目标类方法:method.invoke(目标类,args) 示例 接口 interface IHello{ public void sayHello(); public void sayBye(); } 实现类 class Hello implements IHello{ public void sayHello() { System.out.println("原始Hello方法"); } public void sayBye() { System.out.println("原始Bye方法"); } } 代理并测试 public static void main(String[] args) { final IHello hello = new Hello(); IHello myHello = (IHello)Proxy.newProxyInstance( TestProxy.class.getClassLoader(), //使用指定类加载器加载代理类对象 hello.getClass().getInterfaces(), //要代理的接口 new InvocationHandler() { //代理类对象方法被调用时,执行invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果是sayHello方法,则执行自定义代码 if(method.getName().equals("sayHello")){ System.out.println("增强Hello方法"); return null; } //其它方法执行原实现类代码 method.invoke(hello, args); return null; } }); myHello.sayHello(); myHello.sayBye(); }
java高级2
最新推荐文章于 2019-05-01 18:45:46 发布