Java高薪基础加强
扫描二维码
随时随地手机看文章
一枚举:
为什么要有枚举:
问题:要定义星期几或性别的变量。该怎么定义?
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量在方式在开发阶段无法实现这一目标。
用普通类如何实现枚举功能,定义一个weekday的类来模拟美剧功能:
私有的构造方法
每一个元素分别用一个共有的静态成员变量表示
可以有若干共有方法或抽象方法,例如:要提供nextDay方法必须是抽象的。
采用抽象方法定义nextDay就将大量的if else语句转移成了一个个独立的类。
如果想在一个类中编写完各个枚举类和测试调用类,那么可以将枚举类定义成调用类的内部类。
枚举的基本应用:
扩展:枚举类的values,valueOf,name,toString,ordinal等方法。
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,
public class EnumTest {
public static void main(String[] args) {
WeekDay weekDay2 = WeekDay.FRI;
System.out.println(weekDay2);
System.out.println(weekDay2.hashCode());
System.out.println(weekDay2.name());
System.out.println(weekDay2.ordinal());
System.out.println(WeekDay.valueOf("SUN").toString());
System.out.println(WeekDay.values().length);
//System.out.println(WeekDay.valueOf(, "SUN"));
}
public enum WeekDay
{
SUN,MON,TUE,WED,THI,FRI,SAT;
private WeekDay(){System.out.println("frist");}
private WeekDay(int i){System.out.println("second");}
}
}
enum TrafficLamp{ /** * 匿名对象内部类! * 枚举最简单的创建对象方法:RED,YELLOW,GREEN * 对于普通的类而言,创建对象:Person p = new Person(); * */ RED(30){ @Override public TrafficLamp nextLamp() { return YELLOW; } }, YELLOW(40){ @Override public TrafficLamp nextLamp() { return GREEN; } }, GREEN(45){ @Override public TrafficLamp nextLamp() { return RED; } }; public abstract TrafficLamp nextLamp(); private int time; private TrafficLamp(int time){ this.time = time; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } } public class EnumDemo4 { public static void main(String[] args) { System.out.println(TrafficLamp.RED.nextLamp()); System.out.println(TrafficLamp.RED.nextLamp().getTime()); } }
//=================================================
二、内省JavaBean
JavaBean是一种特殊的Java类,主要用于传输数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,可以通过setter和getter方法来确定。
命名规则:setId()的属性名->id
isLast()->last
setCPU->CPU
getUPS->UPS
总之,一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
2,对JavaBean的简单内省操作
ReflectPoint pt1 = new ReflectPont(3,5);
String propertyName = "x";
x->X->getX->MethodGetX
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());//JavaBean的属性描述类
Method methodGetX = pd.getReadMethod();
Objec reVal = methodGetX.invoke(pt1);
System.out.println(retVal);
Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(pt1,7);
System.out.printn(pt1.getX());//通过普通Java类的方法获得
3,对JavaBean的复杂内省操作
采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的x属性。在程序中把一个类当作JavaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的
BeanInfo对象封装了把这个类当作JavaBean看的结果信息。
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor [] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds ){
if(pd.getName().equals(propertyName)){
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}
return retVal;
4,使用BeanUtils工具包操作JavaBean
先导入beanutils包,在导入logging包,BeanUtils包把上述操作进行封装
BeanUtils.setProperty(pt1,"x","9");
BeanUtils.setProperty(pt1,"birthday.time","111");//birthday为Data类型,可以级联操作,操作对象的对象。birthday有setTime方法。
ProprtyUtils.setProperty(pt1,"x",8);
注意:BeanUtils类get属性时返回的结果是字符串,set属性时可以接受任意类型的对象,通常使用字符串。
用PropertyUtils类get属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型。
//========================================================
三、了解和入门注解的应用
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加则等于没有某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的
类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
看java.lang包,可看到jdk中提供的最基本的annotation.
@Override重写注解
@SuppressWarnings 编译器警告注解
@Deprecated 作废的注解
注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,得先准备好了这个注解类。就像你要调用某个类,得先有开发好这个类。
应用:定义一个最简单的注解:public @interface MyAnnotation{}
把它加在某个类上:@MyAnnotation public class AnnotationTest{}
用反射进行测试AnnotationTest的定义上是否有@MyAnnotation
@MyAnnotation
public class AnnotationTest{
public static void main(String [] args) throws Exception{
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation annotation = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class)
System.out.println(anntation);
}
}
}
@Retention元注解有三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;
分别对应:java源文件,class文件,内存中的字节码
@Override、@SuppressWarnings是源文件期,@Deprecated是内存中的字节码
@Target元注解
Target的默认值为任何元素,可以用数组方式设置{ElementType.METHOD,ElementType.TYPE}//ElementType为枚举类,TYPE代表类的类型
定义基本类型的属性和应用属性:
在注解类中增加String color();
@MyAnnotation(color="red")
用反射方式获得注解对应的实例对象后,再通过该对象调动属性对象的方法。
为属性指定缺省值:String color() default "yellow";
value属性:
String value() default "xxx"; 如果注释中有一个名称为value的属性,且只想设置value属性,那么可以省略value=部分,例:@MyAnnotation("dd").
//========================================================
四、类加载器
类加载器:加载类的工具
在java中用到一个类,类加载器把文件字节码问价从硬盘加载到内存
类加载器也是JAVA类,因为其他java类的类加载器本身也是被类加载器加载,显然必须有第一个类加载器不是java类,这是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类加载器
对象或者默认采用系统类装载器为其父级类加载。
类加载器的委托机制:
当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundExcepiton,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多个儿子,去找哪一个呢?
每个ClassLoader本身只能分别加载特定位置和目录中的类,但他们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。
类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正
的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundExcepiton异常。
面试题:能不能自己写个类叫java.lang.System,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是
总是使用爸爸们能找到的类,这样总是使用java系统提供的System.
//========================================================
五、代理
程序中的代理:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法的运行时间、事务管理、等等。 代理类要调用目标类的功能。 目标类 doSomeThing(){ 业务功能代码 } 代理类 doSomeThing(){ //前置系统功能代码 目标对象.doSomeThing() //后置系统功能代码
}
动态代理:
动态代理的工作原理: 1)Client(客户端)调用代理,代理的构造方法接受一个InvocationHandler,client调用代理的各个方法,代理的各个方法请求转发给刚才通过构造方法传入的handler对象,又把各请求分发给目标的相应的方法。就是将handler封装起来,其中this引用了当前的放(发来什么请求就接受哪个方法)。 猜想分析动态生成的类的内部代码: 1、动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。 2、构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢? 实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下: 分析为什么动态类的实例对象的getClass()方法返回了正确结果呢? 为何动态类的实例对象的getClass()方法返回了正确结果,而没调用invoke方法: 因为代理类从Object上继承了许多方法,其中只对三个方法(hashCode、equals和toString)进行开发,委托给handler去自行处理,对于它身上其他方法不会交给代理类去实现,所以对于getClass()方法,还是由Object本身实现的。即proxy3.getClass(),该是什么结果还是什么结果,并不会交给invoke方法处理。 自定义代理类的步骤: 方式1: 获得动态代理类的字节码文件 Class claz=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); //通过反射获得该动态代理类的构造方法 Constructor c=claz.getConstructor(InvocationHandler.class); //编写一个InvocationHandler类 class myInvocationHandler implements InvocationHandler{ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub return null; } } //创建实例,接受一个invocationhandler对象 c.newInstance(new myInvocationHandler()); 方式2,newProxyInstance这个方法需要三个参数,可以直接创建target的代理对象 Object proxy3 = Proxy.newProxyInstance( target.getClass().getClassLoader(), /*new Class[]{Collection.class},*/ //获取target上的接口 target.getClass().getInterfaces(), new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*long beginTime = System.currentTimeMillis(); Object retVal = method.invoke(target, args); long endTime = System.currentTimeMillis(); System.out.println(method.getName() + " running time of " + (endTime - beginTime)); return retVal;*/ //把上边的代码封装到一个类中,让后调用该类的方法,就实现了方法的封装 advice.beforeMethod(method); Object retVal = method.invoke(target, args); advice.afterMethod(method); return retVal; } } ); return proxy3; } }