20145312 《Java程序设计》第十周学习总结

学习笔记

Chapter 17反射与类加载器

17.1 运用反射

.class文档反应了类基本信息,因而从Class等API取得类信息的方式就称为反射。

17.1.1 Class与.class文档
  1. java.lang.Class的实例代表Java应用程序运行时加载的.class文档。
  2. 可以通过Object的getClass()方法,或者是通过.class常量取得每个对象接口。
  3. 在取得Class对象后,就可以操作Class对象的公开方法,取得基本信息。
代码如下:
package Reflection;

import static java.lang.System.out;
public class ClassInfo {
public static void main(String[] args){
Class clz=String.class;
out.println("类名称:"+clz.getName());
out.println("是否为接口:"+clz.isInterface());
out.println("是否为基本类型:"+clz.isPrimitive());
out.println("是否为数组对象:"+clz.isArray());
out.println("父类名称:"+clz.getSuperclass());
}
}
结果如下:
类名称:java.lang.String
是否为接口:false
是否为基本类型:false
是否为数组对象:false
父类名称:class java.lang.Object
  1. 使用类声明参考名称并不会加载.class文档,可设计测试类来验证。
  2. Some类定义了static区块,默认首次加载.class文档时会执行静态区块。通过文本模式下显示类型,了解何时加载.class文档.
代码如下:
package Reflection;

import static java.lang.System.out;
public class SomeDemo {
public static void main(String[] args){
Some s;
out.println("声明Some参考名称");
s=new Some();
out.println("生成Some实例");
}
}
结果如下:
声明Some参考名称
载入Some.class文档
生成Some实例
17.1.2 使用Class.forName()

使用Class.forName()方法实现动态加载类,可用字符串指定类名称来获得类相关信息。

代码如下:
package Reflection;

import static java.lang.System.out;
public class InfoAbout {
public static void main(String[] args){
try{
Class clz=Class.forName(args[0]);
out.println("类名称:"+clz.getName());
out.println("是否为接口:"+clz.isInterface());
out.println("是否为基本类型:"+clz.isPrimitive());
out.println("是否为数组对象:"+clz.isArray());
out.println("父类名称:"+clz.getSuperclass());
}catch (ArrayIndexOutOfBoundsException e){
out.println("没有指定类名称");
}catch (ClassNotFoundException e){
out.println("找不到指定的类"+args[0]);
}
}
}

或者可以使用forName()第二个版本,将initialize设定为false,这样会在建立类实例时执行static区块。

代码如下:
package Reflection;

import static java.lang.System.out;
class Some2{
static {
System.out.println("[执行静态区块]");
}
}
public class SomeDemo2 {
public static void main(String[] args) throws ClassNotFoundException{
Class clz=Class.forName("Reflection.Some2",false,SomeDemo2.class.getClassLoader());
out.println("已载入Some2.class");
Some s;
out.println("声明Some参考名称");
s=new Some();
out.println("生成Some实例");
}
}
结果如下:
已载入Some2.class
声明Some参考名称
[执行静态区块]
生成Some实例
17.1.3 从Class获得信息

取得Class对象后,就可以取得.class文档中记载的信息:

包对应类型是:java.lang.Package
构造函数对应类型是:java.lang.reflect.Constructor
方法成员对应类型是:java.lang.reflect.Method
数据成员对应类型是:java.lang.reflect.Field

例如取得指定String类的包名称:

Package p=String.class.getPackage();
System.out.println(p.getName()); //显示java.lang
17.1.4 从Class建立对象

如果不知道类名称,就利用Class.forName()动态加载.class文档,取得Class对象后,利用newInstance()方法建立实例,如:

Class clz=Class.forName(args[0]);
Object obj=clz.newInstance();

想采用影片链接库来播放动画,利用接口定义出影片链接库该有的功能。

代码如下:
package Reflection;

import java.util.Scanner;
public class MediaMaster {
public static void main(String[] args) throws ClassNotFoundException,InstantiationException,IllegalAccessException{
String playerImp1=System.getProperty("Reflection.playerImp1");
Player player=(Player) Class.forName(playerImp1).newInstance();
System.out.println("输入想播放的影片:");
player.play(new Scanner(System.in).nextLine());
}
}
package Reflection;

public class ConsolePlayer implements Player {
@Override
public void play(String video){
System.out.println("正在播放"+video);
}
}

指定-DReflection.PlayerImp1=Reflection.ConsolePlayer。

执行结果如下:
输入想播放的影片:Harry Potter
正在播放:Harry Potter
17.1.5 操作对象方法与成员
  1. 使用 invoke()方法来动态调用指定的方法。
代码如下:
package Reflection;

public class Student {
private String name;
private Integer score;
public Student(){}
public Student(String name,Integer score){
this.name=name;
this.score=score;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setScore(Integer score){
this.score=score;
}
public Integer getScore(){
return score;
}
}
17.1.6 动态代理
静态代理

在静态代理实现中,代理对象与被代理对象必须实现统一接口,在代理对象中可以实现日志服务。如下可定义一个Hello接口:

package Reflection;

public interface Hello {
void hello(String name);
}
如果有个HelloSpeaker类操作了Hello接口:
package Reflection; public class HelloSpeaker implements Hello{
public void hello(String name){
System.out.printf("哈啰,%s%n",name);
}
}

在HelloSpeaker类中没有日志程序代码,日志程序代码会放在代理对象中,代理对象同样也要实现Hello接口。

代码如下:
package Reflection;

import java.util.logging.*;
import java.util.logging.Logger;
public class HelloProxy implements Hello {
private Hello helloObj; public HelloProxy(Hello helloObj){
this.helloObj=helloObj;
}
public void hello(String name){
log("hello方法开始...");//日志服务
helloObj.hello(name); //执行商业规则
log("hello方法结束...");//日志服务
}
private void log(String msg){
// Logger logger=Logger.getLogger(HelloProxy.class.getName()).log(Level.INFO,msg);
}
}

静态代理必须为个别接口操作个别代理类,多的代理接口定义多个代理对象,操作和维护代理对象会有不少的负担。

动态代理
  1. 反射API中提供动态代理相关类,该机制可使一个处理者代理多个接口的操作对象。
  2. 处理者类必须操作java.lang.reflect.InvocationHandler接口。
代码如下:
 package Reflection;

import java.lang.reflect.*;
import java.util.logging.*;
public class LoggingHandler implements InvocationHandler {
private Object target; private Object bind(Object target){
this.target=target;
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
Object result=null;
try{
log(String.format("%s()呼叫开始...",method.getName()));
result=method.invoke(target,args);
log(String.format("%s()呼叫结束...",method.getName()));
}catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e){
log(e.toString());
}
return result;
}
private void log(String message){
Logger.getLogger(LoggingHandler.class.getName()).log(Level.INFO,message);
}
}

接下来可使用LoggingHandler的blind()方法来绑定被代理对象:

package Reflection;

public class ProxyDemo {
public static void main(String[] args){
LoggingHandler loggingHandler=new LoggingHandler();
Hello helloProxy=(Hello) loggingHandler.blind(new HelloSpeaker());
helloProxy.hello("Justin");
}
}

17.2了解类加载器

17.2.1 类加载器级层架构
  1. 加载器的分类及作用:
Bootstrap Loader:产生Exrtended Loader和System Loader
Exrtended Loader:父加载器为Bootstrap Loader
System Loader:父加载器为Exrtended Loader
  1. Bootstrap Loader通常由C撰写而成。若是Oracle的JDK,Bootstrap Loader会搜索系统参数sun.boot.class.path中指定位置的类,默认是JRE目录的classes中的.class文档,或lib目录中.jar文档里的类。

  2. Exrtended Loader由Java撰写而成,会搜索系统参数java.ext.dirs中指定位置的类,默认是JRE目录lib\ext\classes中的.class文档。

  3. System Loader由Java撰写而成,会搜索系统参数java.class.path中指定位置的类,也就是CLASSPATH路径,默认是当前工作路径下的.class文档。

  4. 加载类是会以Bootstrap Loader->Exrtended Loader->System Loader的顺序寻找类,如果所有类加载器都找不到指定类,就会抛出java.lang.NoClassDefFoundError。

  5. 对null调用getParent()方法会抛出NullPointedException异常。

  6. ClassLoader可以使用loadClass()方法加载类,使用localClass方法加载类时,默认不会执行静态区块,真正使用类建立实例时才会执行静态区块。

  7. 关系代码如下:

package Reflection;

import static java.lang.System.out;
public class ClassLoaderHierarchy {
public static void main(String[] args){
Some some=new Some();
Class clz=some.getClass();
ClassLoader loader=clz.getClassLoader();
out.println(loader);
out.println(loader.getParent());
out.println(loader.getParent().getParent());
}
}
结果如下:
载入Some.class文档
sun.misc.Launcher$AppClassLoader@1b84c92
sun.misc.Launcher$ExtClassLoader@140e19d
null
17.2.2 建立ClassLoader实例
  1. 使用URLClassLoader来产生新的类加载器,需要java.net.URL作为其参数来指定类加载的搜索路径。使用URLClassLoader的loadClass()方法加载指定类时,会先委托父加载器代为搜索。
  2. 由同一类加载器载入的.class文档,只会有一个Class实例。如果同一.class文档由两个不同的类加载器载入,则会有两份不同的Class实例。
代码如下:
package Reflection;

import static java.lang.System.out;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoaderDemo {
public static void main(String[] args){
try {
String path=args[0];
String clzName=args[1]; Class clz1=loadClassFrom(path,clzName);
out.println(clz1);
Class clz2=loadClassFrom(path,clzName);
out.println(clz2);
out.printf("clz1与clz2为%s实例",clz1==clz2 ? "相同":"不同");
}catch (ArrayIndexOutOfBoundsException e){
out.println("没有指定类加载路径与名称");
}catch (MalformedURLException e){
out.println("加载路径错误");
}catch (ClassNotFoundException e){
out.println("找不到指定的类");
}
}
private static Class loadClassFrom(String path,String clzName)throws ClassNotFoundException,MalformedURLException{
ClassLoader loader=new URLClassLoader(new URL[] {new URL(path)});
return loader.loadClass(clzName);
}
}

path可以输入不在System Loader以上层级类加载器搜索路径的其他路径。

Chapter 18 自定义泛型、枚举与注释

18.1 自定义泛型

泛型定义:
仅定义在方法上的泛型语法
用来限制泛型可用类型的extends与super关键字
?类型通配字符的使用
18.1.1 使用extends与?
  1. 若extends之后指定了类与接口,想再指定其他接口,可以使用&连接。
代码如下:
package Generics;

import java.util.Arrays;
public class Sort {
public static <T extends Comparable<T>> T[] sorted(T[] array){
T[] arr=Arrays.copyOf(array,array.length);
sort(arr,0,arr.length-1);
return arr;
}
private static void sort(Object[] array,int left,int right){
if(left<right){
int q=partition(array,left,right);
sort(array,left,q-1);
sort(array,q+1,right);
}
}
private static int partition(Object[] array,int left,int right){
int i=left-1;
for(int j=left;j<right;j++){
if(((Comparable) array[j]).compareTo(array[right])<=0){
i++;
swap(array,i,j);
}
}
swap(array,i+1,right);
return i+1;
}
private static void swap(Object[] array,int i,int j){
Object t=array[i];
array[i]=array[j];
array[j]=t;
}
}
package Generics;

public class SortDemo {
public static void main(String[] args){
String[] strs={"3","2","5","1"};
for(String s:Sort.sorted(strs)){
System.out.println(s);
}
}
}

结果如下:

1
2
3
5
  1. 如果B是A的子类,而Node< B>可视为一种Node< A>,则称Node具有共变性或有弹性的。Java泛型不具有共变性,可以使用类型通配字符?与extends来声明变量,使其达到类似的共变性。
  2. 若声明?不搭配extends,则默认为? extends Object。

    Node<?> node = null;//相当于Node<? extends Object>
  3. java的泛型语法在执行时期实际上只会知道是Object类型,由于无法在执行时期获得类型信息,编译程序只能就编译时期看到的类型来做检查,因而造成以上谈及的限制。
18.1.2 使用super与?
  1. 如果B是A的子类,而Node< A>可视为一种Node< B>,则称为Node具有逆变性。Java泛型不具有逆变性,可以使用类型通配字符?与super来声明,使其达到类似的逆变性的效果。
  2. 若泛型类或接口不具共变性或逆变性,则称为不可变的或严谨的。

18.2 自定义枚举

18.2.1 了解java.lang.Enum类
  1. 直接撰写程序继承Enum类会被编译程序拒绝。
  2. Enum是个抽象类,无法直接实例化,它操作了Comparable接口。Action的构造函数被声明为private,因此只能在Action类中调用。
  3. 在JDK1.4之前撰写的API,仍是使用interface定义常数作为枚举值。
  4. Enum的equals()方法与hashCode()方法基本上继承了Object的行为,但被标示为final。由于标示为final,所以定义枚举是,不能重新操作equals()与hashCode(),这是因为枚举成员,在JVM中智慧存在单一实例,Object定义的equals()与hashCode()作为对象相等性比较是适当的定义。
18.2.2 enum高级运用
  1. values()方法,将内部维护Action枚举实例的数据复制后返回。如果想要知道有哪些枚举成员,就可以使用这个方法。
  2. Enum类可以自行定义构造函数,但不得为公开构造函数,也不可以在构造函数中调用super()。
  3. 在enum中调用构造函数比较特别,直接在枚举成员后加上括号,就可以指定构造函数需要的自变量。
  4. 在static区块中,编译程序仍自行维护name与ordinal的值,接着才是调用自定义构造函数时传入的value值。
  5. 特定值类本体语法:在枚举成员后,直接加上{}操作Command的execute()方法,这代表着每个枚举实例都会有不同的execute()曹组欧,在职责分配上,比switch的方式清楚许多。特定值类本体语法不仅在操作接口时可以使用,,也可以运用在重新定义父类方法。

18.3 关于注释

18.3.1 常用标准注释
  1. @Override

    就是标准注释,被注释的方法必须是父类或接口中已定义的方法,请编译程序协助是否真的为重新定义方法。
  2. @Deprecated

    如果某个方法原先存在与API中,后来不建议再使用,可以在该方法上注释。若有用户后续想调用或重新定义这个方法,编译程序会提出警告。对于支持泛型的API,建议明确指定泛型真正类型,如果没有指定,编译程序会提出警告。
  3. @SuppressWarnings

    指定抑制unchecked的警告产生:@SuppressWarnings(value={"unchecked"})
  4. @SafeVarargs

    表明开发人员确定避免了heap pollution问题。heap pollution问题就是编译程序无法检查执行时期的类型错误,无法具体确认自变量类型。
  5. @FunctionalInterface

    让编译程序可协助检查interface是否可做为lambda的目标类型
18.3.2 自定义注释类型
  1. 标示注释:就是注释名称本身就是信息,对编译程序或应用程序来说,主要是检查是否有注释出现,并作出对应的动作。

    2.相关规则:
(1)如果注释名称本身无法提供足够信息,设置单值注释
(2)注释属性也可以用数组形式指定。
(3)在定义注释属性时,如果属性名称为value,则可以省略属性名称,直接指定值。
(4)对成员设定默认值,使用default关键字即可。
(5)要设定数组默认值,可以在default之后加上{},必要时{}中可放置元素值。
  1. 定义注释时,可使用java.lang.annotation.Target限定时可指定java.lang.annotation.ElementType的枚举值。
  2. 在制作JavaDoc文件时,默认不会将注释数据加入文件中,如果想要将注释数据加入文件,可以使用java.lang.annotation.Documented。
  3. 默认父类设定的注释,不会被继承至子类,在定义注释时,设定java.lang.annotation.Inherited注释,就可以让注释被子类继承。
18.3.3 JDK8标注增强功能
  1. ElementType的枚举成员是用来限定哪个声明位置可以进行标注。在JDK8中,增加了两个枚举成员TYPE _PARAMETER、TYPE _USE。
  2. ElementType.TYPE _ USE可用于标注在各式类型,一个标注如果被设定为ElementType.TYPE_USE,只要是类型名称,都可以进行标注。
  3. @Repeatable

    可以让你在同一个位置重复相同标注
  4. @Filters

    作为收集重复标注信息的容器,而每个@Filters储存各自指定的字符串值。
18.3.4 执行时期读取注释信息
  1. 自定义注释,默认会将注释信息存储于.class文档,可被编译程序或位码分析工具读取,但执行时期无法读取注释信息,在执行时期读取注释信息,可以使用java.lang.annotation.Retention搭配java.lang.annotation.RetentionPolicy枚举指定。
  2. RetentionPolicy为RUNTIME的时机,在于让注释在执行时期提供应用程序信息,可使用java.lang.reflect.AnnotatedElement接口操作对象取得注释信息。
  3. JDK 8中新增了getDeclaredAnnotation()、getDeclaredAnnotationsByType()、getAnnotationsByType()三个方法。

    getDeclaredAnnotation()可以让你取回指定的标注,在指定@Repeatable的标注时,会寻找收集重复标注的容器。

    getDeclaredAnnotationsByType()、getAnnotationsByType()就不会处理@Repeatable的标记。

20145312 《Java程序设计》第十周学习总结的更多相关文章

  1. 201521123027 <java程序设计>第十周学习总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 异常: 多线程: 2.书面作业 Q1.finally 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 ...

  2. Java程序设计第十周学习总结

    Java课程知识梳理: 流的区分; 字符流与字节流的区别: 字节流是直接操作文件本身的,如果没有关闭字节流操作,文件会依然输出内容 而字符流在程序运行之后会发现文件没有任何的内容,这是因为字符流操作的 ...

  3. 20145312 《Java程序设计》第九周学习总结

    20145312 <Java程序设计>第九周学习总结 学习笔记 Chapter 16整合数据库 16.1 JDBC入门 16.1.1 JDBC简介 SUN公司为了简化.统一对数据库的操作, ...

  4. 20145213《Java程序设计》第九周学习总结

    20145213<Java程序设计>第九周学习总结 教材学习总结 "五一"假期过得太快,就像龙卷风.没有一点点防备,就与Java博客撞个满怀.在这个普天同庆的节日里,根 ...

  5. 《Java程序设计》第九周学习总结

    20145224 <Java程序设计>第九周学习总结 第十六章 整合数据库 JDBC入门 ·数据库本身是个独立运行的应用程序 ·撰写应用程序是利用通信协议对数据库进行指令交换,以进行数据的 ...

  6. 20145236 《Java程序设计》第九周学习总结

    20145236 <Java程序设计>第九周学习总结 教材学习内容总结 第十六章 整合数据库 JDBC简介 1.JDBC是java联机数据库的标准规范.它定义了一组标准类与接口,标准API ...

  7. 20155304田宜楠2006-2007-2 《Java程序设计》第一周学习总结

    20155304田宜楠2006-2007-2 <Java程序设计>第一周学习总结 教材学习内容总结 - 浏览教材,根据自己的理解每章提出一个问题 第一章 除了书上提到的开发工具还有什么适合 ...

  8. 20145237 《Java程序设计》第九周学习总结

    20145237 <Java程序设计>第九周学习总结 教材学习内容总结 第十六章 整合数据库 JDBC入门 ·数据库本身是个独立运行的应用程序 ·撰写应用程序是利用通信协议对数据库进行指令 ...

  9. 学号 20175212 《Java程序设计》第九周学习总结

    学号 20175212 <Java程序设计>第九周学习总结 教材学习内容总结 一.MySQL数据库管理系统 1.在官网上下载并安装MySQL 2.在IDEA中输入测试代码Connectio ...

  10. 20172325 2018-2019-2 《Java程序设计》第九周学习总结

    20172325 2018-2019-2 <Java程序设计>第九周学习总结 教材学习内容总结 图的定义 图是由顶点集(VertexSet)和边集(EdgeSet)组成,针对图G,顶点集和 ...

随机推荐

  1. LAMP集群项目五 项目备份

    1.打包到本地 2.推送到备份服务器 3.删除若干天前的备份 ip=`awk '/IPADDR/' /etc/sysconfig/network-scripts/ifcfg-eth0 |awk -F ...

  2. jQuery设置内容和属性方

    何问起 hovertree.com 设置内容 - text().html() 以及 val()text() - 设置或返回所选元素的文本内容html() - 设置或返回所选元素的内容(包括 HTML ...

  3. 160406、mybatis批量删除

    <deleteid="deleteCTQ" parameterType="java.lang.String"> DELETE FROM sqm_pr ...

  4. Java中分页功能源码实例

    一.源码(后附使用说明) package com.zhiyou100.crm.util; /** * 分页功能 * @author YangXianSheng * */ public class Pa ...

  5. Code Forces 21 A(模拟)

    A. Jabber ID time limit per test 0.5 second memory limit per test 256 megabytes input standard input ...

  6. kubernetes使用中遇到的坑

    随着kubernetes的发展现在使用的范围越来越广,在使用过程中碰到问题是避免不了的,有些时候一些坑能提前避免是最好的,下面我做一个小记录,把我们生产环境中遇到的坑总结下,方便后面查询同时也方便各位 ...

  7. inode file 结构

    inode位图(inode Bitmap) 和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用. inode表(inode Table) 我们知道,一个文件除了数据需要存储之外, ...

  8. 解Bug之路-TCP粘包Bug

    解Bug之路-TCP粘包Bug - 无毁的湖光-Al的个人空间 - 开源中国 https://my.oschina.net/alchemystar/blog/880659 解Bug之路-TCP粘包Bu ...

  9. 新团建立时间 timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

    w 不根据当前时间戳更新. `wtime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,

  10. Intellij idea中maven加载jar包很慢的解决方案.

    默认加载的都是国外的源,我们可以配置国内的源. 右键项目-->maven-->Open ''setting.xml'' 复制下面的代码进去.保存. 我这里使用的版本是 ideaIU-14. ...