java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类加载器负责加载特定位置的类。
BootStrap,ExtClassLoader,AppClassLoader。
类加载器也是java类,因为其它是java类的类加载器本身也需要被类加载器加载,显然必须有第一个类加载器
不是java类,来加载这些是java类的类加载器,这个加载器就是BootStrap。
类加载器的委托机制:
当需要加载一个类时,类加载器使用委托机制,这是类加载器会委托给它爸爸去找,它爸爸在委托它爸爸的爸爸。一直委托到它们的祖宗。
然后从祖宗开始一级一级向下找,知道找到了便停止,如果没找到便抛异常。
一级一级的从上向下找,避免了重复加载,浪费内存。
代理:23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。
代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,
而代理对象可以在客户端和目标对象之间起到中介的作用。
java虚拟机可以生成实现一个或多个接口的类的动态代理,
CGLIB(第三方插件):可以实现所有类的动态代理。
下面是一个代理的演示类:
public class ProxyTest {
public static void main(String[] args) throws Exception {
Object target=new ArrayList<String>();//代理要操作的目标。
MyAdvice myA=new MyAdvice();//作为一个程序员实际要开发的程序类。 打个比方,比如你想买一个联想笔记本,这时你找到一个代理商,
//你除了要告诉代理商你要买的东西是联想笔记本即目标target,还要告诉他,你要什么型号的,外表是什么颜色的等一些事情。这些事情代理
//不可能知道,所以是我们程序员要做的事情。
Collection Proxy2 =(Collection) getObject(target,myA);//创建代理类,这个代理给的目标是一个集合,建议是打印出每操作一次集合的时间。
Proxy2.add("sss");//通过代理调用集合。
Proxy2.add("kkk");
Proxy2.add("ppp");
Proxy2.size();
System.out.println(Proxy2.size());
System.out.println(Proxy2.toString());
}
private static Object getObject(final Object target,final Advice log) {//封装好的代理类。这段代码一但写好,是不用改变的。这就是代理的好
//处。
Object Proxy2=Proxy.newProxyInstance(target.getClass().getClassLoader(),//目标类的加载器
target.getClass().getInterfaces(),//目标类实现的接口的类字节码集合
new InvocationHandler(){ //这里是一个内部类,实际上代理即使通过这个类的invoke方法,来对目标(ArrayList集合)
public Object invoke(Object proxy, Method method, Object[] args)//和Advice建议(打印方法运行时间)来进行操作的。
throws Throwable { //
log.beforeMethed();
Object reget=method.invoke(target, args);
log.afterMethed();
return reget;//将操作后的结果返回给调用代理的方法。
}
});
return Proxy2;
}
}
interface Advice{//代理需要的建议的格式。也就是说你必须实现这个接口才能提建议。为什么要实现这个接口呢,也很好理解。
void beforeMethed(); //继续拿买笔记本举例,你告诉代理商买个笔记本,建议是要有4个轱辘。代理商不久傻逼了吗。
void afterMethed();//这时代理商就要弄一个表格,里面只有。机子型号,机身颜色等几个内容。不按规格写的直接忽略。
}//这个表格就相当于建议接口。
class MyAdvice implements Advice{//这个更好理解了吧,就是你填写完的表格。代理建议的具体实现。
long beginTime; //实际开发中,当把代理做成AOP框架后,我们需要做的就是写这部分的代码。
@Override //然后把目标和Advice配置到配置文件中就好了。
public void afterMethed() {
// TODO Auto-generated method stub
long afterTime= System.currentTimeMillis();
System.out.println("执行时间为...."+(beginTime-afterTime));
}
@Override
public void beforeMethed() {
// TODO Auto-generated method stub
beginTime=System.currentTimeMillis();
}
}
枚举:
枚举相当于一个类,其中也可以定义构造方法,成员变量,普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后面有分号与其它成员分隔。
带构造方法的枚举,构造方法必须私有。
如果有多个构造函数,该如何让选择调用哪个构造函数呢?
直接在枚举元素后面加()并在里面传入对应的参数即可。
带方法的枚举;
普通方法直接实现,抽象方法在元素的匿名内部类中实现。每个元素都是枚举子类的实例对象。
枚举只有一个成员时,就可以作为一种单例的实现方式。
枚举示例:
enum TrafficLamp {
RED(25){
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return GREEN;
}
}
,GREEN(30) {
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return YELLOW;
}
},YELLOW(5) {
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return RED;
}
};
int time=0;
public abstract TrafficLamp nextLamp();
private TrafficLamp(int time){this.time=time;}
}
上述枚举演示: 主方法:
public static void main(String[] args) throws InterruptedException {
TrafficLamp tp=TrafficLamp.RED;
while(true){
Thread.sleep(100*tp.time);
tp=tp.nextLamp();
System.out.println(tp);
}
}
反射。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,
都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Class:
java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class。
每个Class实例对象都是个各类对象的字节码。
那么如何得到各个字节码对应的实体对象呢??
1,Class als=类名.class。
2,Class als=对象.getClass();
3,Class als=Class.forName("java.util.Date");//必须是绝对路径。
8个基本类型和void,对应9个预定义(primitive )Class实体对象。
预定义的Class实体对象,还可以用基本类型的TYPE方法获得。
Class cls=integer.TYPE;
Class cls=int.class;
这2个是相等的。
在这里我们可以通过字节码来判断这个类是什么类型。
int[].class.isArray();
Constructor:代表某个类的一个构造方法。(某个类的一个构造方法的反射类)
得到某个类所有的构造方法:
Constructor constructor=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
Constructor constructor=Class.forName("java.lang.String").getConstructors(StringBuffer.class);
创建实体对象:
通常方式:String s=new String(new StringBuffer("abc"));
反射方式:String s=(String)constructor.newInstance(new StringBuffer("abc"));
Class.newInstance();方法。
例子:String s=(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部代码怎么写的呢??用到了缓存机制来保存默认构造方法的实例。
Field:代表某个类的一个字段。(某个类的一个字段的反射类)
示例:
先创建一个准备用来被反射操作的类。
class ReflectYuan{
private int i;
public int j;
public ReflectYuan(int i,int j){
this.i=i;
this.j=j;
}
}
Field fieldI=ReflectYuan.class.getDeclaredField("i");//当类中字段是私有的时候用getDeclaredField("字段名");获取该字段。
Field fieldJ=ReflectYuan.class.getField("j");
ReflectYuan ry=new ReflectYuan(3,5);
fieldI.setAccessible(true);//该值设置为true。表示当使用反射机制时,取消Java的语言访问检查。
system.out.println(fieldI.get(ry));
system.out.println(fieldJ.get(ry));
字段(field)反射的综合应用。
public static void main(String[] args) throws Exception {
ReflectYuan ry1=new ReflectYuan(3,5);
ReflectYuan ry2=new ReflectYuan(1,5);
Field[] fields=ReflectYuan.class.getFields();
for(Field field : fields){
if(field.getType()==String.class){//因为比较的是字节码,字节码只有一份,所以用==比较。
String oldValue=(String)field.get(ry1);
String newValue=oldValue.replace('a', 'b');
field.set(ry2, newValue);
}
}
System.out.println(ry2.toString());
}
class ReflectYuan{
private int i;
public int j;
public String str1="abcd";
public String str2="abcdaac";
public String str3="abcdbba";
public ReflectYuan(int i,int j){
this.i=i;
this.j=j;
}
public String toString(){
return "str1="+str1+"str2="+str2+"str3="+str3;
}
}
Method类代表某个类中的一个成员方法。
得到类中的某一个方法:
Method charAt=Class.forName("java.lang.String").getMethod("",int.class);
调用该方法:
charAt.invoke(str,1);
jdk1.4和1.5的区别:
public Object invoke(Object obj,Object ... args);
public Object invoke(Object obj,Object[] args);
利用反射调用其它类中的main方法:
public static void main(String[] args) throws Exception {
Class cls1=huoDeClass(RsflectMain1.class);//只要将参数改成RsflectMain2.class就会执行RsflectMain2的主方法。
//反射的好处得到初步体现,只需改变一个参数,就可以执行不同的主方法。
Method RsflectMainMethod1=cls1.getMethod("main", String[].class);
RsflectMainMethod1.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});
}
public static Class huoDeClass(Class cls){
return cls;
}
}
class RsflectMain1{
public static void main(String[] args){
for(String s:args){
System.out.println(s);
}
}
}
class RsflectMain2{
public static void main(String[] args){
System.out.println("haha,我的第一个反射");
}
}
数组与Object的关系。
int[] i=new int[2];
Integer[] in=new Integer[2];
int[][] a=new int[2][3];
Object obj=i;
//Object[] obj1=i;这个是错误的编译都不会通过。
Object[] obj2=in;
Object[] obj3=a;//这个obj3里面装的int[]数组。
数组的反射应用:
Array就是数组反射后的类对象,里面有操作数组的方法,可以在多看看。
static void printObject(Object obj) {
Class cls=obj.getClass();
if(cls.isArray()){
int num=Array.getLength(obj);
for(int i=0;i<num;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
hashCode:通过哈希算法得到的值,这个值能保证我们在对底层是哈希表的集合存入数据时,数据的唯一性。同时经过哈希算法内部处理,能够使查找速度更快。
但是这样也会在我们初学的 时候,导致内存泄漏等问题(当我们存入数据后,在改变参与哈希运算的元素的值的时候,就会导致内存泄露)。
示例:
public static void main(String[] args) throws Exception {
Collection collection1=new ArrayList();
Collection collection2=new HashSet();
ReflectYuan ry1=new ReflectYuan(3,3);
ReflectYuan ry2=new ReflectYuan(4,4);
ReflectYuan ry3=new ReflectYuan(3,3);
collection1.add(ry1);
collection1.add(ry2);
collection1.add(ry3);
collection1.add(ry1);
collection2.add(ry1);
collection2.add(ry2);
collection2.add(ry3);
collection2.add(ry1);
//ry1.i=5;//如果加上这就话,就会该变hashCode码值,导致下面删除语句找不到删除对象,从而导致内存泄漏。
collection2.remove(ry1);
System.out.println(collection1.size());
System.out.println(collection2.size());
}
class ReflectYuan{
//private int i;
public int i;
public int j;
public ReflectYuan(int i,int j){
this.i=i;
this.j=j;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + i;
result = prime * result + j;
System.out.println(result);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectYuan other = (ReflectYuan) obj;
if (i != other.i)
return false;
if (j != other.j)
return false;
return true;
}
}
泛型:
泛型是给编译器看的,对于运行时,泛型不起作用,也就是我们可以通过反射,来绕过泛型。
示例:
Collection<Integer> arr=new ArrayList<Integer>();
//arr.add("sss");//因为应用了泛型,当我们直接添加不属于泛型指定类型的时候,编译器会报错,编译失败。
Collection.class.getMethod("add", Object.class).invoke(arr, "sss");//反射运行时才会起作用,所以可以跳过编译器,
//向集合内添加非泛型指定数据。
System.out.println(((ArrayList)arr).get(0));
关于泛型我的一点看法:
看了张老师的视频,视频里面提到集合数组不让加泛型。但是我试了下是可以的。
示例:Vector<Integer>[] vv=new Vector[4];
Vector vt1=new Vector<Integer>();
Vector vt2=new Vector<String>();
Vector vt3=new Vector();
vv[0]=vt1;
vv[1]=vt2;
vv[2]=vt3;
//以上三种情况是都可以添加成功的。也就是说后面带不带泛型都可以存到数组里面。
vv[0].add("ss");//编译不通过
vv[1].add("ss");//编译不通过
vv[2].add("ss");//编译不通过
vv[0].add(9);//编译通过
vv[1].add(9);//编译通过
vv[2].add(9);//编译通过
//但是当我们存数据的时候就会发现,只能存入Integer型数据,字符串是不能通过编译的。
//从而可以看出数组里面的集合泛型已经起作用了。
再看下面一个示例:
Vector vt4=new Vector<String>();
vt4.add("ss");//编译通过
vt4.add(9);//编译通过
//我们发现当前面没有加泛型的时候,后面的泛型不起作用,无论什么数据都可以添加进去。
下面还有一个示例:
Vector vt5=new Vector<String>();
Vector<Integer> vt6=vt5;//编译通过
vt5.add(9);//编译通过
vt5.add("9");//编译通过
vt6.add(9);//编译通过
vt6.add("9");//编译不通过
//看到这我们会发现,带泛型的集合对象是可以传给带泛型的集合类。并且添加数据,以带参数的类为主。
Vector<Integer> vt7=new Vector();
Vector<String> vt8=vt7; //编译不通过
Vector vt9=vt7;//编译通过
vt9.add("9");//编译通过
看到这我们发现泛型只要用在前面就好了,后面根本不起作用。其实不然,后面的也是有作用的。看下面的示例:
boolean b=new Vector<String>().add(9);//编译不通过
boolean b=new Vector<String>().add("9");//编译通过
当集合为匿名集合对象调用方法的时候,泛型变起作用了。嘿嘿虽然这种用法基本很少,但是还是找到一点用处。
泛型的通配符应用(?)。
使用通配符可以引用其它的各种参数化的类型,但是不能调用与参数化有关的方法,只能调用与参数化无关的方法。
限定通配符<? extends T> 参数只能是T和T的子类,向下限定。
<? super T>参数只能是T和T的父类,向上限定。
泛型综合应用。
Map<String, Integer> map=new HashMap<String, Integer>();
map.put("zhangsan", 12);
map.put("lisi", 16);
map.put("wangwu", 32);
Set<Map.Entry<String, Integer>> entry=map.entrySet();
for(Map.Entry<String, Integer> en:entry){
System.out.println(en.getKey() +"=="+ en.getValue());
}
自定义泛型的一些应用:
public static void main(String[] args)throws Exception {
Object obj="hhhh";
String s= autoCon(obj);
System.out.println(s);
HashSet hs=new HashSet();
hs.add("fff");
hs.add("fhg");
hs.add("ffu");
sop(hs);
}
public static <T>T autoCon(Object t){
return (T)t;
}
public static < T> void sop(Collection<T> t){
Iterator<T> it=t.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
for (T tt:t) {
System.out.println(tt);
}
}
参数的类型推断:
当我们定义的泛型被多出应用了,泛型以返回值类型和带<T>中的类型为主,其它处的对象类型只能是<? extends T>的对象类型。
通过反射获得泛型的实际参数类型。(有局限性,只能获得方法参数所带的泛型)
public static void main(String[] args)throws Exception {
Method methed=FanXingDemo.class.getMethod("setVector", Vector.class);
Type[] type=methed.getGenericParameterTypes();
ParameterizedType pty=(ParameterizedType)type[0];
System.out.println(pty);
}
public void setVector(Vector<String> v){}