黑马程序员 高新部分

来源:互联网 发布:淘宝店铺没有流量怎么办 编辑:程序博客网 时间:2024/05/01 15:20

-----------android培训java培训、java学习型技术博客、期待与您交流! -----------

1,静态导入。

import 导入某一个包,某一个类。
import static 导入静态成员。
当类名重名时,需要指定具体的包名。
当方法重名时,指定具备所属的对象或类。

2,可变参数:
当一个方法接受的参数个数不固定时,可用可变参数:...args

注意:
1,可变参数只能在参数列表最后。
2,...位于变量类型和变量名之间,前后有无空格都可以。
3,调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
eg:若干个整数累加:
public static int add(int x,int... args){int sum = x;for(int i =0;i<args.length;i++){       sum +=args[i];}return sum+x;}
3,增强for循环。
for(type变量名:集合变量名){...}
注意:
1,迭代变量必须在()中定义
2,集合变量可以是数组或实现了Iterable接口的集合类(能迭代的类)。
eg:public static int add(int x,int... args){int sum = 0;for(int i :args){       sum +=i;}return sum+x;}
4,基本数据类型的自动拆箱与装箱。
对应基本类型数据,系统会自动把数据进行对象与数据的转换。
eg:Integer i = new Integer(3);
Integer iObj = 3;这时系统会自动把3装成Integer对象。这是装箱。
int b = iObj+4;这时系统会自动把iObj对象转换成3。这是拆箱。

当数字在一个字节之内(-128~127)时,系统会把该数字放在缓存里,
Integer a =90;Integer b =90;
System.out.println(a==b);结果是true.
Integer a =200;Integer b =200;
System.out.println(a==b);结果是false.
当对一个字节内的数字进行装箱时,系统会把该数字放在缓存里。
当该数字超过一个字节,就直接装箱成对象。
这里涉及到享元模式:
该模式的思想是:把一个较小的,经常被使用的,且不怎么改变的对象。不必经常创建对象,只需要建立一次,然后需要时被调用。


枚举:

让某个类型的变量的取值只能为若干个固定值的一个。否则编译报错。

自定义实现枚举:
public abstract class WeekDay {//将构造函数私有,不能创建对象private WeekDay(){}//用匿名类的方式将其抽象方法复写,返回一个WeekDay类型的成员public final static WeekDay SUN = new WeekDay(){public WeekDay nextDay(){return MON;}};public final static WeekDay MON = new WeekDay(){public WeekDay nextDay(){return SUN;}};public abstract WeekDay nextDay();//复写toString()public String toString(){return this==SUN?"SUN":"MON";}}public class Enumeration {public static void main(String[] args) {WeekDay w = WeekDay.SUN;System.out.println(w.nextDay());}}
由此可见,枚举只有一个成员的时候,就可以作为一种单例的实现方式。
枚举的实例对象可以由不同的构造函数建立。下面代码中做解释。
枚举实例:
public class Enumeration {public static void main(String[] args) {// TODO Auto-generated method stubWeekDay1 w1 = WeekDay1.TUE;System.out.println(w1.nextDay());WeekDay w = WeekDay.TUE;System.out.println(WeekDay.TUE);System.out.println(w.name());//打印自己的名字System.out.println(w.ordinal());//打印自己的排行System.out.println(w.equals(WeekDay.MON));//比较是否是同一天System.out.println(w.getClass());//获取自己的类名System.out.println(WeekDay.valueOf("SUN").toString());//将字符串变成对应的枚举元素System.out.println(WeekDay.values());//将枚举中的元素装到WeekDay[]中。方便遍历/*匿名对象的构造方法选择。 new Text(){子类内容};其子类调用的是父类的空参数构造函数 new Text(10){子类内容};子类调用的是父类的有参的构造函数。  */}public enum WeekDay{//SUN,MON,TUE,WES,THU,FRI,SAT//如果后面还有内容,最后需要加“;”SUN(3),MON(),TUE,WES,THU,FRI,SAT;//元素的定义要放在第一行private WeekDay(){System.out.println("first");}private WeekDay(int day){System.out.println("second");}//两种构造函数,如果括号里面有数值,就是使用的第二种。}public enum TrafficLamp{RED(30){public TrafficLamp nextLamp(){return GREEN;}},GREEN(40){public TrafficLamp nextLamp(){return YELLOW;}},YELLOW(3){public TrafficLamp nextLamp(){return RED;}};//这里的抽象类是用来限制枚举的元素。枚举类前面不能加abstractpublic abstract TrafficLamp nextLamp();private int time;private TrafficLamp(int time){this.time = time;}}}


反射:把JAVA类中的各种成分映射成对应的java类。

Class类:反射的基础。
java中将的众多的类当作一个对象,定义了一个Class类来描述这些对象。
Class cl1 = Date.class//Class类型变量cl1指向的是Date.class在内存中的字节码
Class cl2 = Person.class//Class类型变量cl2指向的是Date.class在内存中的字节码
Person p = new Person();
一个对象获取其本身类的字节码,有三种方式
1,用p.getClass();
2,Class.forName("一个类的全称")。当这个字节码被加载过,可以直接返回。如果没有被加载过,就被类加载器加载,缓存到虚拟机里面。这一种方法用来做反射。
3,类名.class。例如System.class

八中基本数据类型,加上void返回类型都有对象的Class对象。
只要是在源程序中出现的类型,都有各自的Class实例对象。
String str = "abc";
Class cl1 = str.getClass();
Class cl2 = String.class;
Class cl3 = Class.forName("java.lang.String");
System.out.println(cl1==cl2);
System.out.println(cl3==cl2);
-------
System.out.println(cl1.isPrimitive());//false判断使否是基本数据类型。
System.out.println(int.class.isPrimitive());//true
System.out.println(int.class == Integer.class);//false
System.out.println(int.class == Integer.TYPE);//true   Integer.TYPE返回的是int的class实例对象。
System.out.println(int[].class.isArray());

Constructor类:代表某个类中的构造方法
得到某个类所有的构造方法:Constructor[] con = String.class.getConstructors(); 同时还有getDeclaredConstructors
得到某个类中的指定构造方法:getConstructor(构造方法参数的class对象)  
getDeclaredConstructor(构造方法参数的class对象)可以获取被保护的构造函数。
用setAccessible(true);使被保护的构造函数可见。
Constructor con = String.class.getConstructor(StringBuffer.class);//获取String类中构造参数为StringBuffer类的构造函数。
String s = (String)con.newInstance(new StringBuffer("abcd"));用指定的构造函数创建一个实例对象。
注意:因为没有指定泛型,所以需要反射建立的对象强转成String类型。
Class<?>[] getParameterTypes() 获取构造函数的参数类型。

Field类 获取对象中的字段。成员变量

FieldDemo f = new FieldDemo(20, "Fack");
Field f1 = f.getClass().getDeclaredField("age");//获取指定"age"名称的字段,它可以不是public的。
f1.setAccessible(true);
System.out.println(f1.get(f));//获取某个对象中"age"名称字段的内容。

eg:将某个对象的字符串字母b换成a
涉及方法:
getField()
getFields():获取全部公共字段,存入数组中,
getDeclaredField()
getDeclaredFields():获取所有声明的字段,存入数组
setAccessible(boolean):是否强制访问调用的对象所指的成员。
static setAccessible(AccessibleObject[] array,boolean flag)
思路:
1,获取对象中所有的成员变量
2遍历这些变量,选取String类型的
3,获取这些变量的值
4替换字母
5将替换后的值赋给对象。 
public static void changeChar(Object obj){Field[] f1 = obj.getClass().getDeclaredFields();Field.setAccessible(f1,true);System.out.println(f1.length);for(Field field : f1){if(field.getType()==String.class)//因为字节码是唯一的,用==比用equals方法好。{try {String oldString = (String) field.get(obj);String newString = oldString.replace("b", "z");field.set(obj, newString);} catch (Exception e){e.getStackTrace();}}}}class FieldDemo{private int age;public String name;String str1 = "abcd";String str2 = "abbd";String str3 = "abbb";public FieldDemo(int age, String name) {super();this.age = age;this.name = name;}}


Method类:获取某个类中的成员方法。


Method method = String.class.getMethod("charAt", int.class);//获取String类中的char(int)方法
System.out.println(me.invoke(f.str1, 2));//使用该方法。获取f.str1中的第三个字符。
如果invoke()方法的第一个参数为Null,则表示该方法为静态方法

getParameterTypes()获取方法中的参数的类型

反射MainDemo类中的main方法
Method mainMethod = MainDemo.class.getMethod("main", String[].class);
mainMethod.invoke(null, (Object)new String[]{"123","1234","12321"});//其中,编译器会对传入的数组进行拆包,导致编译失败(传入的参数不匹配)
解决方式两个:1,将数组转成Object类;2,将数组装入新数组中new String[]{new String[]{"123","1234","12321"}};
class MainDemo{public static void mian(String[] args){System.out.println("反射出来了"):}}class Run{public static void main(String[] args){Method m = Maindemo.class.getMethod("main",String[].class);m.invoke(null,(Object)new String[]{});}}

反射数组:
先判断对象是否是数组类型,再通过Array类中的方法获取数组的属性。
public static void printObj(Object obj){Class cl = obj.getClass();if (cl.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);}}
获取数组的类型:
1,先得到数组中的元素,a[0]

2, 获取元素的类型,返回字符串:a[0].getClass.getName();


反射的作用:实现框架功能。
内存泄漏
:当使用HashSet集合时,因为是按照HashCode方法来算出hash值进行判读元素。当对存入的元素的hash值进行修改时,会导致原有的元素残留在内存中,会导致内存泄漏。
用反射开发框架的原理:通过反射将某个类提前写入框架中,当实现功能时,加载该类即可。

简单实现框架:
public static void main(String[] args)throws Exception{//读取文件中的配置信息。InputStream in = new InputStream("text.properties");/*注意:获取文件要用完整的路径,此路径是运算得来的,不是强行给予的。或者通过类加载器用getResourceAsStream()加载文件。*/Properties p = new Properties();p.load(in);in.close();//反射得到className对应的类的对象。Collection collection = Class.forName(p.getProperty("className")).newInstance();collection.add("123");collection.add("1233");collection.add("1233");}

内省:IntroSpector

JavaBean:一种特殊的类,用来传递信息。方法通常是set和get。它的属性往往是除去set,get字符的值。javaBean中一定要有一个空参数的构造方法。
如果取出后的名称第二个字母是小写的,则把第一个字母变成小写。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称为值对象(Value Object,简称VO)


对JavaBean的简单内省操作:

1,用PropertyDescriptor 类,将需要查找的属性名,和被查找的类传进去。
2,调用getReadMethod和getWriteMethod方法分别获取被查找类中的get,set方法。
3,通过反射,使用这两个方法。
public class JavaBeanDemo {public static void main(String[] args) throws Exception{StringDemo sd = new StringDemo("Bean",1);PropertyDescriptor pd = new PropertyDescriptor("name", sd.getClass());Method methodGet = pd.getReadMethod();Object getVal = methodGet.invoke(sd);System.out.println(getVal);Method methodSet = pd.getWriteMethod();methodSet.invoke(sd, "bean");System.out.println(sd.getName());}}

稍复杂的方式:
1,使用Introspector类中的getBeanInfo()方法。
2,使用BeanInfo中的静态方法:getPropertyDescriptors()将属性装进数组中。
3,遍历数组,查找符合的属性,
BeanInfo bi = Introspector.getBeanInfo(sd.getclass());//通过内省得到某个类的BeanInfo对象PropertyDescriptor[] pds = bi.getPropertyDescriptors();Object getVal = null;for(PropertyDescriptor pd:pds){if(pd.getName().equals(propertyName)){Method methodGet = pd.getReadMethod();Object getVal = methodGet.invoke(sd);;break;}} return retval;

可以使用BeanUtils工具包的setProperties()和getProperties()等方法进行更简便的操作。

注解:

@Deprecated(过时)  @Override(覆盖)  @SuppressWarnings(阻止警告)
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。以后java编译器,开发工具,其它程序就可以通过反射来了解你的类或者元素上有无何种标记。标记可以夹在包,类,字段,方法,方法的参数以及局部变量上。

注解也是一种特殊的类
定义一个注解
public @interface AnnotationDemo{}
元注解:注解的注解。
引入@Retention(保留的意思)元注解,其三种取值:RetetionPolicy.SOURCE;RetetionPolicy.CLASS;RetetionPolicy.RUNTIME
注明该注解存在的时间段。
@Override 生命周期在SOURCE阶段
@Deprecated 生命周期在RUNTIME阶段
@SuppressWarnings 生命周期在SOURCE阶段

@Target(ElementType.METHOD)声明该注解作用在方法上。
@Target(ElementType.TYPE)声明该注解作用在类上。
@Target({ElementType.METHOD,ElementType.TYPE})声明该注解作用在方法和类上。

给注解添加属性:
1,value类型的属性;
在注解类中加入属性,eg:定义抽象方法 String value();
在使用注解时,设置属性@MyAnnotation(value="dffed")
如果该值是默认的,String value() default "  ";使用时,可以不用设置。
2,数组类型的属性;
添加数组:int[] array();
如果数组中只有一个元素,可以直接写成array=1;不需要大括号
3,枚举类型的属性;
添加枚举:添加Enumeration类中的TrafficLamp枚举
Enumeration.TrafficLamp lamp() default Enumeration.TrafficLamp.RED;
4,注解类型的属性。
MetaAnnotation anno();
eg:
public @interface MyAnnotation
{
String value();
String color() default "blue";
int[] array() default {1,2,3};
Enumeration.TrafficLamp lamp() default Enumeration.TrafficLamp.RED;
MetaAnnotation anno() default @MetaAnnotation(" ");
}
添加注释:@注释
@MyAnnotation(color="red",value="haha",array=1,anno=@MetaAnnotation("a"))


泛型,前面有讲过。这里说一下获取某个方法中的参数的泛型类型
注意:泛型的定义类型,只能是引用类型,不能是基本类型。
 编译器在编译完成后会去除泛型。
eg:
DemoReflectP类中有一个方法public void method2(Vector<String> v){}
Method me = DemoReflectP.class.getMethod("method2",Vector.class);Type[] types = me.getGenericParameterTypes();//获取method2方法的形参类型。ParameterizedType type = (ParameterizedType)types[0];//得到表示Vector的参数类型。System.out.println(type.getRawType());//得到type所表示的类型。System.out.println(type.getActualTypeArguments()[0]);//获取Vector中的参数类型

类加载器:加载硬盘上的.class文件。

BootStrap主加载器,(JRE/lib/rt.jar)System类就是它加载的。
ExtClassLoader,(JRE/lib/ext/*.jar)
AppClassLoader(ClassPath指定的所有JAR或目录)

类加载器的委托机制:
1,首先当前线程的类加载器去加载线程中的第一个类。
2,如果类A中引用了类B,java虚拟机将使用加载类A的类加载器来加载类B
3,还可以直接调用ClassLoader.loadClass()方法来指定某个加载器来加载某个类。
4,加载某个类的时候会想让其上级的类去加载,当所有其上级加载器都找不到的时候,返回给发起者,再找不到就会报异常。

自定义加载器,需继承ClassLoader
loadClass方法:此方法内部先让上级类查找并加载。
findClass方法:我们需要自己复写该方法。
defineClass方法:将数组的内容变成class对象。
public Class findClass(String name) {
             byte[] b = loadClassData(name);
             return defineClass(name, b, 0, b.length);
         }
复写findClass方法:将文件转换成字节存入数组中,然后通过defineClass生成class对象。
eg:加密一个class文件
prviate static void cypher(InputStream ips,OutputStream ops){int b =-1;while((b=ips.read())!=-1){ops.write(b^34);}}

解密文件:
public Class findClass(需解密的文件){try{InputStream ips = new InputStream(需解密的文件);ByteArrayOutputStream bos = new ByteArrayOutputStream();cypher(ips,bos);//这里加密函数也是解密函数。ips.close();byte[] bytes = bos.toByteArray();return defineClass(解密的文件,bytes,0,bytes.length);}catch(Exception e){}}


*******************************************************************************************

代理和动态代理  AOP(面向方面编程)

代理:类似现实中的中间商概念。代理从中可以对货物进行自己的操作。而JAVA中也有代理,可以对反射得到的方法进行修饰。

动态代理: JVM可以在运行期动态生成类的字节码,这种动态生成往往被用作代理类,即动态代理类.
注意:JVM生成的动态类只能用作具有相同接口的目标类的代理,所以JVM生成的动态类必须实现一个以上的接口。
而CGLIB库可以动态生成一个类的子类作为该类的代理。
动态代理机制: 动态代理的实现主要用到InvocationHandler(调用处理器)这个接口

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
通过接口的类加载器加载一个指定接口的代理类实例,返回该代理类实例,通过InvocationHandler(调用处理器)这个接口将方法让目标类运行。
InvocationHandler 是代理实例的调用处理程序 实现的接口。 
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。 

获取Collection的动态代理类:
//通过Proxy类中的getProxyClass方法。获取class对象Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);System.out.println(clazzProxy1.getName());//获取该类的构造函数:Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);// T newInstance(Object... initargs) 泛型限定。Constructor<T>,所以建立出来的对象要强转成Collection类型Collection con = (Collection)constructor.newInstance(new MyInvocationHandler1());class MyInvocationHandler1 implements InvocationHandler{public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{return null;}}

方法二:使用Proxy中的,newProxyInstance(代理类的加载器,接口数组,InvocationHandler对象)

Collection con2 = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),new Class[]{Collection.class} ,new InvocationHandler(){ArrayList target = new ArrayList();public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {long beginTime =System.currentTimeMillis(); for(int x=0;x<90;x++){System.out.print("1");}Object retVal = method.invoke(target,args);long endTime =System.currentTimeMillis(); System.out.println("Running time ="+(endTime-beginTime));return retVal;}});con2.add("aaa");System.out.println(con2.size());System.out.println(con2.getClass().getName());//这里打印出来的是代理的类名。不是目标对象的类名。//是因为在继承Object类的时候,只有hashCode,equals,toString方法是交给handler做,其它的都由它自己实现。


将上面代码进行优化,使其可以获取用户指定的代理。
private static Object getProxy(final Object target,final Advicer advice){Object proxy = Proxy.newProxyInstance(target.getClass.getClassLoader,target.getClass.getInterfaces(),new InvocationHandler(){public Object invoke(Object proxy,Method method,Object[] args){advice.beforeMethod();Object value = method.invoke(target,args);advice.endMethod();return value;}});}public class Advicer{Long startTime = 0;public void beforeMethod(){startTime = System.currentTimeMills();}public void endMethod(){System.out.println(System.currentTimeMills()-startTime);}}
-----------android培训java培训、java学习型技术博客、期待与您交流! -----------