• Java高新技术3(框架,JavaBean与内省(Introspector))


    1.Java框架(frame)

    /*
    通俗例子:
     我做房子(框架)卖给用户住,由用户自己安装门窗和空调(用户自定义类/用户自定义其它信息)
     用户需要使用我的房子(框架),把符合框架中结构的门窗插入进我提供的框架中.
    框架与工具类区别:
     框架调用用户提供的类
     工具类被用户的类调用
    示例:
             利用反射运行指定的某个类中的main方法,
             通过arg[0]来接收要运行的类名,也就是说
             我已写好这个功能,而你要运行的类还不存在
            我这个功能可以提前编译,你只需在运行时提供给我要运行的类即可.
    框架要解决的核心问题:
         我若干年前写的程序调用你若干年后写的程序->反射机制
    为什么要用框架?
        框架相当于半成品->也就是说提高开发效率
    */

    模拟框架:利用反射机制读取配置文件

    public class FrameMN {
     public static  String loadProp()throws IOException{   
        
      Properties property=new Properties();
      
      BufferedInputStream bis=null;
      InputStream is=null;
      try{
      /* bis=new BufferedInputStream
               (new FileInputStream("config.properties"));//这里的根目录为工程名称(JavaEnhance)
                                                         //"config.properties"相当于".\config.properties"
    */    
    
        System.out.println(System.getProperty("java.class.path"));//classpath路径  
    
       /*    
       bis=new BufferedInputStream(FrameMN.class.getClassLoader().getResourceAsStream
                                  ("com/itheima/day2/config.properties"));//将会在classpath+指定的路径(com/itheima/day2/config.properties)
                                                                         //下查找,com前面不能有/->将不再是相对路径
       */     
     bis=new BufferedInputStream(FrameMN.class.getResourceAsStream("/com/itheima/day2/config.properties"));
      
      property.load(bis);    
      return property.getProperty("className");
       }
      finally{
        if(bis!=null)
          bis.close();
      }
     }
     
    public static void main(String[] args)throws Exception{
          String className=loadProp();
          Collection collections=(Collection)Class.forName(className).newInstance();
          collections.add("3");
          collections.add(2);
          System.out.println(collections);//[3, 2]
          System.out.println(System.getProperty("user.dir"));
       }
    }

    目录结构:

    目录

    注意几点:

    /*
    config.properties 该如何配置其位置?
    1.相对路径
       其实就是System.getProperty("user.dir");
       相对路径随时有可能在变,但是可以通过 System.getProperty("user.dir")来获取当前程序执行所在的相对路径.
      但是如果这个文件不再当前路径下->找不到引发IO异常
    2.绝对路径
      从盘符开始路径:d:abc1.txt
      不建议使用这样做缺点:用户没有d:盘符呢?
    解决办法:
     例如:把某个软件安装到某个目录->通过方法获取到其安装路径->在与内部的配置文件拼接形成绝对路径
    一个错误:
      Frame.class.getClass()
    */
    /*
      1.eclipse在你src下新建.java文件后它会自动编译成.class文件放在bin目录下
      2.当在src下创建文件->eclipse将该文件拷贝一份到bin目录下  
             在src某个包下创建文件->eclipse将该文件拷贝一份到bin目录的相同包下
    */
    /*
    
     注意:
     Class类:
     public InputStream getResourceAsStream(String name)
     此方法委托此对象的类加载器。如果此对象通过引导类加载器加载,
     则此方法将委托给 ClassLoader.getSystemResourceAsStream(java.lang.String)。 
    在委托前,使用下面的算法从给定的资源名构造一个绝对资源名: 
    
    如果 name 以 '/' 开始 ('u002f'),则绝对资源名是 '/' 后面的 name 的一部分。 
    否则,绝对名具有以下形式: 
       modified_package_name/name
    其中 modified_package_name 是此对象的包名,该名用 '/' 取代了 '.' ('u002e')。 
    
    
    例如:
    <截图>
     配置文件和运行类在同一个包下:
      1. getResourceAsStream("/com/itheima/day2/config.properties")
        相当于调用ClassLoader.getResoureceAsStream("com/itheima/day2/config.properties")
      2.getResourceAsStream("config.properties")
        ->com/itheima/day2/config.properties(此字节码对象的包名+指定的路径)->
        ClassLoader.getResoureceAsStream("com/itheima/day2/config.properties")
     配置文件和运行类不在同一包下:
       1.在com.itheima.day2.resources下
         依然用Class的getResourceAsStream("resource/com/itheima/day2/config.properties");
       2.如果在com.ithmeima.day1下
         "/com/itheima/day1/config.properties"
     */

    读取配置文件

      2.JavaBean与Introspector

    /*
     JavaBean与内省(Introspector):
     1.JavaBean是一种特殊的Java类,主要用于传递数据信息,这种Java类中的方法
        主要用于访问私有字段,且方法名符合某种命名规则.
    2.如果在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例
    通常称之为值对象(Value Object).这些信息在类中用私有字段来存储,如果读取货设置这些字段的值
    则需要通过一些相应的方法来访问,这些方法该如何命名?
      JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量.如果方法
    名为setId,意为设置id,至于你存到哪个变量上,用管吗?getId意为获取id,至于从哪个变量上得到的,用管吗?
    去掉set前缀后,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小写.
        setId属性名->id
        isLast属性名->last
        setCPU属性名->CPU     
     总而言之:一个类被当做JavaBean使用时,JavaBean的属性是根据方法名推断出来的
             它根本看不到java类内部的成员变量
    
    一个符合JavaBean特点的类可以被当做普通类一样进行使用,但把它当做JavaBean用肯定需要带来一些额外的好处
    好处:
     1.在JavaEE开发中,经常使用JavaBean.很多环境要求按JavaBean方式进行操作
     2.JDK提供了对JavaBean进行操作的一些API,这套API就称为内省.
    */
    //eclipse 4.3下自动生成getter和setter方法
    //JavaBean的属性是由getter或setter方法决定
    //只要含有其中的一个方法就是JavaBean的属性
    //User类继承Object的方法getClass,因此还有一个属性为class
    class User{
        private String userName;//字段
        private String uName;//字段
        private String CPU;
        private String controlProcessUnited;
        private int x;
        
    
        public int getX() {
            return x;
        }
        public void setX(int x) {
            this.x = x;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public String getuName() {
            return uName;
        }
        public void setuName(String uName) {
            this.uName = uName;
        }
        public String getCPU() {
            return CPU;
        }
        public void setCPU(String cPU) {
            CPU = cPU;
        }
        public String getControlProcessUnited() {
            return controlProcessUnited;
        }
        public void setControlProcessUnited(String controlProcessUnited) {
            this.controlProcessUnited = controlProcessUnited;
        }
        
    }

    对JavaBean内省操作:

    用一个测试类:Car

    package com.itheima.day2;
    
    import java.util.Date;
    
    public class Car {
      private String color;
      private int  number;//轮胎个数
      private Date birthday=new Date();//为了测试 设置级联属性
      public Car(String color, int number) {
        super();
        this.color = color;
        this.number = number;
      }
      public String getColor(){
          return color;
      }
      public Date getBirthday() {
        return birthday;
      }
      public void setBirthday(Date birthday) {
        this.birthday = birthday;
     }
    public void setColor(String color) {
            this.color = color;
        }
      public int getNumber() {
            return number;
        }
      public void setNumber(int number) {
            this.number = number;
        }
      
    }
    package com.itheima.day2;
    import java.lang.reflect.Method;
    import java.beans.BeanInfo;
    import java.beans.Introspector;
    import java.beans.PropertyDescriptor;
    
    
    public class IntrospectorDemo {
    
        public static void main(String[] args)throws Exception{
            // TODO 自动生成的方法存根
            //方式一:使用反射操作JavaBean
            //获取颜色->color->color有一个单词第二个字母小写->推断出方法名getColor
            Car car=new Car("红色",4);
            Method method=car.getClass().getMethod("getColor");
            Object retVal=method.invoke(car);
            System.out.println(retVal);//"红色"
            
            //方式二:使用内省操作JavaBean->不用再推断方法名
            PropertyDescriptor pd=new PropertyDescriptor("color",car.getClass());//第一个参数属性名,第二个参数把哪一个类当成JavaBean类
            method=pd.getReadMethod();//将获取到的getColor方法封装成Method对象
            retVal=method.invoke(car);
            System.out.println(retVal);//"红色"
            
            method=pd.getWriteMethod();//将获取到的setColor方法封装成Method对象
            method.invoke(car,"黑色");
            System.out.println(car.getColor());//验证是否改掉//"黑色"
            
            //测试封装后的方法
           System.out.println(getProperty("number",car));//4
           setProperty("number",car,10);
           System.out.println(car.getNumber());//10
         }
       
        
        //对上面的操作步骤进行封装提高复用性
        public static Object getProperty(String property,Object obj) throws Exception{//获取指定对象的属性值
           PropertyDescriptor pd=new PropertyDescriptor(property,obj.getClass());
            Method method=pd.getReadMethod();
            return method.invoke(obj);
        
        /*//方法二:
              BeanInfo bi=Introspector.getBeanInfo(obj.getClass());//在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件,描述目标 bean 的 BeanInfo 对象。 
              PropertyDescriptor[] pdArrs=bi.getPropertyDescriptors();//获取到该JavaBean中所有属性信息,BeanInfo没有获取单个属性的方法
              for(PropertyDescriptor pdArr : pdArrs)
                if(pdArr.getName().equals(property)){//获取到属性描述的属性名称(getName),遍历
                  Method method=pdArr.getReadMethod();
                  return method.invoke(obj);
                }
               return null;*/
          
        }
       public static void setProperty(String Property,Object obj,Object value)throws Exception{
            PropertyDescriptor pd=new PropertyDescriptor(Property,obj.getClass());
            Method method=pd.getWriteMethod();
            method.invoke(obj,value);
       }
      
    }

    使用开源工具BeanUtils来操作JavaBean:

    /*
     使用开源BeanUtils来更方便操作JavaBean
     1.从http://commons.apache.org/beanutils/下载commons-beanutils-1.8.3-bin.zip
     2.将其中的.jar导入工程
     3.不采用BuildPath->添加外部归档方式导入工程,这样做.jar并不在工程目录下(例如在d:下)
          一旦将工程拷贝到其它机器下,还需要把该.jar拷贝到d:下,不然用不了
          解决方式:在工程下新建lib文件夹->将.jar拷贝到lib下->Build Path
       
     */
    /* 
     public static String getProperty(Object bean,String name)
    throws IllegalAccessException,
           InvocationTargetException,
           NoSuchMethodException
    public static void setProperty(Object bean,String name,Object value)
      throws IllegalAccessException,
           InvocationTargetException*/
    
    public class BeanUtilsDemo {
       
        public static void main(String[] args)throws Exception {
            // TODO 自动生成的方法存根
           Car car=new Car("black",20);
           
           //使用BeanUtils来set/get属性
           String number=BeanUtils.getProperty(car,"number");//获取car对象color属性的值,注意返回值固定为String
           BeanUtils.setProperty(car,"color","red");//设置car对象number属性的值为4 
           BeanUtils.setProperty(car,"number","2");//传入2(自动装箱,内部需要拆箱)或"2"均可,"2"内部涉及到从String->int转换
           System.out.println(car.getColor()+" "+car.getNumber()+"
    ");
           
           
           //级联属性设置与获取
           BeanUtils.setProperty(car,"birthday.time","1000");//在Date类中一个public void setTime(long time)方法->属性time
                                                               //通俗例子:设置person.head.face.eye.color
           System.out.println(BeanUtils.getProperty(car,"birthday.time")+"
    ");
           
          
           //JavaBean与Map相互转换
           Map map=BeanUtils.describe(car);//会将JavaBean中 所有属性值 存入到Map中,前提是该属性值有对应get方法
           System.out.println(map);
    //
    里面还有一个属性为字节码文件对象,也就是指定的JavaBean类
           map=new HashMap();
           map.put("color","blue");
           map.put("number",2);
           map.put("birthday.time",200);
           BeanUtils.populate(car, map);//填充:将map中key对应javabean中的property,将其value赋给property
           System.out.println(car.getColor()+" "+car.getNumber());
           System.out.println(BeanUtils.getProperty(car,"birthday.time")+"
    ");
           
           
           //PropertiesUtils工具类
           PropertyUtils.setProperty(car,"number",13);//属性值的类型必须和Car中number类型一致均为int
           Object obj=PropertyUtils.getProperty(car, "number");
           System.out.println(obj+" "+obj.getClass().getName());//说明上面的getProperty以Integer类型返回
        } 
    
    }
    /*
    不导入commons-logging-1.1.3.jar之前的运行结果:
    Exception in thread "main" java.lang.NoClassDefFoundError:
    org/apache/commons/logging/LogFactory
    相当于有了电视机,但遥控器是第三方->电视机没法用
    需要导入遥控器->下载commons-logging(通用日志)并build path*/

    BeanUtils

       1:  package cn.itcast.feature;
       2:   
       3:  import java.lang.reflect.InvocationTargetException;
       4:  import java.text.ParseException;
       5:  import java.text.SimpleDateFormat;
       6:  import java.util.Date;
       7:   
       8:  import org.apache.commons.beanutils.BeanUtils;
       9:  import org.apache.commons.beanutils.ConversionException;
      10:  import org.apache.commons.beanutils.ConvertUtils;
      11:  import org.apache.commons.beanutils.Converter;
      12:  import org.apache.commons.beanutils.locale.converters.DateLocaleConverter;
      13:  import org.junit.Test;
      14:   
      15:  /*
      16:   虽然使用BeanUtils简化了代码量 
      17:   但是BeanUtils默认只能帮你进行八大基本类型间的转换或
      18:   String到八大基本类型转换.
      19:   如果我需要用到String->Date的转换,需要我们自定义转换器
      20:   */
      21:  public class CustomConvert {
      22:    private Person p=new Person();
      23:    @Test
      24:    public void convertTest_1() throws IllegalAccessException, InvocationTargetException{
      25:        BeanUtils.setProperty(p,"birthday","1980-3-1");
      26:        System.out.println(p.getBirthday());
      27:    }
      28:    
     JUnit测试:
    StringToDate_1
    2.这时候我们需要自定义转换器完成String->Date
       1:   @Test
       2:    public void convertTest_2() throws IllegalAccessException, InvocationTargetException{
       3:       ConvertUtils.register(//注册一个自定义转换器完成String->Date转换
       4:                  new Converter() {
       5:                      @Override
       6:                      public Object convert(Class type, Object value) {
       7:                          // TODO Auto-generated method stub
       8:                          if (value == null)
       9:                              return null;
      10:                          if (!(value instanceof String))
      11:                              throw new ConversionException("不支持从"
      12:                                      + value.getClass().getName() + "到"
      13:                                      + type.getName() + "的转换");
      14:                          String valueStr = (String) value;
      15:                          if ((valueStr = valueStr.trim()).equals(""))// 去除字符串首尾的空格,同时判断是否是空串
      16:                              return null;
      17:   
      18:                          // 开始转换工作,以上做的判断完全为了程序的健壮性
      19:                          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      20:                          try {
      21:                              return sdf.parse(valueStr);// 该方法有异常声明但是我不能在convert方法上进行声明,只能try...catch
      22:                              // 因为该匿名子类复写的Converter中的convert方法上没有任何异常声明
      23:                          } catch (ParseException e) {
      24:                              // TODO Auto-generated catch block
      25:                              throw new RuntimeException(e);
      26:                          }
      27:   
      28:                      }
      29:   
      30:                  }, Date.class);
      31:      BeanUtils.setProperty(p,"birthday","1980-3-1");
      32:      System.out.println(p.getBirthday());
      33:    }  
      34:    
    StringToDate
     
     
    3.如果把BeanUtils.setProperty(p,"birthday","1980-3-1");换成BeanUtils.setProperty("birthday",1980-3-1);不能通过测试:
    StringToDate_3
     
    4.使用已提供的Sting->Date的转换器:
       1:   @Test
       2:    public void convertTest_3() throws IllegalAccessException, InvocationTargetException{
       3:       //使用API提供的转换器:DateLocaleConverter
       4:       ConvertUtils.register(new DateLocaleConverter(),Date.class);
       5:       BeanUtils.setProperty(p,"birthday","1980-3-1");
       6:       System.out.println(p.getBirthday());
       7:       
       8:       //但是该转换器有Bug,那就是传入一个空串
       9:       BeanUtils.setProperty(p,"birthday","");
      10:       System.out.println(p.getBirthday());
      11:    }
     
    StringToDate_4
     
    5.最后来看下throw new RuntimeException(e);打印的异常信息:
       1:   @Test
       2:   //throw new RuntimeException(Throwable cause);
       3:      /*用指定的原因和详细消息 (cause==null ? null :cause.toString())
       4:      构造一个新的运行时异常(它通常包含类和 cause 的详细消息)。
       5:      */
       6:    //下面我们故意让parse抛出ParseException看下打印信息
       7:    public void ParseExceptionTest(){
       8:          try { 
       9:              System.out.println(new SimpleDateFormat("yyyy/MM/dd").parse("2014-1-17"));
      10:          } catch (ParseException e) {
      11:              // TODO Auto-generated catch block
      12:              throw new RuntimeException(e);
      13:          }
      14:    }
    ParseException
  • 相关阅读:
    SpringBoot集成swagger后出现: Failed to start bean ‘documentationPluginsBootstrapper‘的解决方法
    [转]When allowCredentials is true, allowedOrigins cannot contain the special value “*“
    SpringBoot 集成Swagger后提通过http://localhost:8001/swaggerui.html#/访问得不到页面
    C#窗体开发
    如何查看一个域名所对应的IP地址?
    Vetur can't find `tsconfig.json` or `jsconfig.json` in XXX
    npm 搜索
    查看android 中sqlite数据库的表,发现没有表结构和数据问题
    Another Intro for Cookies
    TypeScript Crash Course: Property Access Modifiers
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3204322.html
Copyright © 2020-2023  润新知