java基础知识一览

     一、关键字:JDK1.5的新特性、Eclipse、MyEclipse、IDE、Workspace、Perspective、view、设置javac和java的版本、模块代码、快捷键、导入工程、静态导入、可变参数、增强for循环、自动装箱与拆箱

  1、 Ecplise的基础设置

  MyEclipse是Eclipse的插件,相当于补丁,增强了一些功能,用于开发JavaEE的程序(JavaEE,Java Platform Enterprise Edition,企业级版本),即WEB项目。现在是将2个软件和在一起安装,更加方便。Eclipse是用Java语言开发的。运行Eclipse,实际是在运行javaw.exe,即图形界面。Eclipse启动后会自动关闭DOS。可以直接通过windows任务管理器关闭javaw.exe来关闭Eclipse。

  2、IDE

  IDE:Integrated Development Environment,集成开发环境。使用工程化(project)方式管理一个项目的开发过程。一个独立的项目就是一个工程,项目中涉及许多java类、资源文件(配置文件)等,在一个工程中进行集中管理。工程框架内部包括,源文件、jar包(第三方类)、资源文件等。不使用工程的情况下,编程人员需要维护这些源文件、源文件与其他文件的目录关系。需要逐一编译这些源文件,并手动运行编译后的结果,非常复杂、非常麻烦。将一个程序的所有源文件用一个工程来组织,开发工具对所有的源文件进行集中管理,记住每个源文件的位置和相互关系。工程中的源文件、启动类、启动参数设置等配置信息都有记录。

   3、Workspace(工作间)

  Javac.exe和java.exe不是Eclipse内嵌的,是可以设置的。配置工作间:工具栏中的Window—》Prefrences(手选项)—》java,编译时使用Compiler(编译器),可以确定JDK的版本;运行时使用Installed JREs,确定jre的版本。不同人开发的不同工程运行在同一个Eclipse,可以有不同的风格,比如可以设置不同快捷键。可以使用工作间Workspace,将一些工程整合成一个整体,统一管理。一个工作间内可以包括多个工程,工作间的设置可以影响旗下的所有工程。

File—》Swtich Workspace,创建工作间在某目录下,没有该目录会自动创建。新工作间创建完成后重启,打开新工作间。也用于切换、导入工作间

创建工程,File—》New—》Project—》JavaProject,取名、设置jre,完成创建。Perspective透视图,即处在java的界面之下。

  导入工程:使用图表Import Project。

工程的内部层次:Project—》SourceFolder—》Package—》class。按照这个顺序,在相应的目录上建立源文件和类文件,会分类整齐。也可以利用工具栏上的图标创建各种文件。

包名需要加上公司域名(倒写),类名需要准确使用英语,创建类时可以直接加入main主函数。更改工程名:右键—》Refactor(重构)—》Rename。工作间下的类都会受到工作间的影响,会删除以前的配置,例如,快捷键。Refactor也可以更改类名,其他应用该类名的地方也会更改,非常高效。

  4、快捷键设置

Window—》Prefrences—》General(整体配置)—》Eidtors(编辑)—》Keys(键盘),RemoveBinding,解除绑定;Apply,应用;Key Sequence,设置快捷键;Command,查看。

明确工作间和工程之间的关系。在工作间进行配置,影响旗下所有的工程

   5、 Perspective与view

    透视图,若干个小窗口的集合,用于调试代码等功能。每一个小窗口称为视图view。一些视图的预定义的集合,称为透视图。切换到某一功能的透视图后,相应的视图就就会出现。例如,java perspective,写代码的透视图。

    调试变量值,或查看变量值,右键—》DebugAs,进入Debug perspective,选择变量点右键,选择Watch,然后Step Into下一步,看到值;或者直接在Debug的透视图下,对该行代码,点右键再次Debug,并下一步,看结果。

    调试完毕后,再回到java perspective,继续写代码。右键—》Run as,在Console运行。

    Window—》Show View,显示视图,调出你需要的视图。Other里面还有更多的视图。

   6、 设置工程的javac和java

对整个工程进行设置:工具栏中的Window—》Prefrences—》java,编译时使用Compiler(编译器),可以确定JDK的版本,Add安装新的JDK;运行时使用Installed JREs,确定jre的版本。需要保证jre版本的统一。

对单个工程进行配置:点右键到properties,Java Compiler,设置jre,即javac.exe版本。点右键—》Run as—》Run—》JRE,查看Runtime JRE版本,即java.exe版本。也可以通过BuildPath设置JDK,方法:右键—》Build Path—》Configure Build Path—》Libraries,添加新的JDK。

“编译—运行”原则:

1、  高版本的java能运行低版本的javac编译的程序。

2、  低版本的java不能运行高版本的javac编程才程序。

    错误:bad version number in .class file,低运行版本。

    所有工程都继承工作间的配置,其中单个工程也可以复写工作间的配置。这就像父类和子类的关系。这是一种思想,也可以用到其他地方。

    设置模块代码

    选择代码—》右键—》Surround With,里面有多个语句的模板可供选择,会将该代码放在语句中。

可以按照自己的需求设置语句,例如,try catch finally。Window—》Prefrences—》java—》Editor—》Templates(模板),通过New Template创建新模板,需要有Name、Description、Pattern,通过Insert Variable添加固定格式。

   

    7、导入工程

    File—》Swtich Workspace,选择需要的工作间导入。

将某一工程导入到工作间的方法:先将工程目录复制到工作间的目录下,然后

1、右键—》Import—》General—》Existing Projects into Workspace(工作间内已有的工程)—》选择需要的工程。

2、图标Import Project—》选择需要的工程。

如果发现新加入的工程无法运行,可能是JDK版本不对,需要重新设置。右键—》Build Path—》Configure Build Path—》Libraries,添加新的JDK。

在Java Build Path窗口下,可以添加jar包到预定义库,也可以直接添加jar包库。在JRE System Library[jre6]预定义库中添加额外的jar包:Add External Jars,添加需要的jar包。或者将需要的jar包添加入一个库中,以后再使用时,直接调用jar包库即可,方法:AddLibrary—》UserLibrary—》建立自己的jar包库,添加需要的jar包,例如处理图片的jar包。

  8、静态导入

普通的import语句:导入一个类或某个包中所有类。不占用资源,只是不用再写包名而已。

静态导入:导入类中的所有静态方法。JDK1.5以后出现的新特性。格式:import static 包名.类名.*;。例如,import static java.lang.Math.*;

注意:新的特性不能在老版本的JDK上运行。

某些方法是Object和工具类共同拥有的,必须说明具体调用者才能正确使用。例如,System.out.println(Arrays.toString(arr));,不说明的话默认是Object的toString()方法。

当类名重名时,需要指定具体的包名;当方法重名时,需要指定具体的类或对象。不加static,导出的都是类;加了static后,导出的都是静态成员。

可变参数VariableParameter

JDK1.5后的新特性,通过一个可以变长度的数组来储存值并进行操作本质上仍是数组,是数组的简写形式。例如

    public static void show(int... arr)

       {

              for(int i : arr)

              {

                     System.out.println(i);

              }

       }

    不用每次都手动建立数组对象,只要将需要操作的元素作为参数传递即可,虚拟机隐式的将这些参数封装成了数组。…代表可变参数。不输入参数也可以。asList,输出的就是可变参数的集合。

    方法的可变参数:可变参数一定要定义在参数列表的最后面。例如,public static void show(String str , int... arr),防止后面的整数自行组成数组。

    overload:重载,函数名一样,参数列表不一样,返回值和函数内容可以一样可不一样。针对的是同一功能但不同参数的情况。JDK1.5以后,有了新特性,可变参数。

    override:重写,子类覆盖父类的方法,函数名、返回值和参数列表都一样,函数内容一定不一样,只有这样才能完全覆盖父类的方法。

可变参数的特点

1、  只能出现在参数列表的最后。

2、  …位于变量类型和变量名之间,前后有无空格都可以。

3、  调用可变参数方法时,编译器为该可变参数隐式创建一个数组,所以在方法体中需要以数组的形式运行可变参数。

JDK1.5以前,没有可变参数,使用数组代替。

  增强for循环

语法:for(type 变量名 :集合变量名){….}

注意:1. 迭代变量必须在()中定义。2. 集合变量可以是数组或实现了Iterable接口的集合类。变量名可以是表达式。

type前面加修饰符,可以访问内部类。

/正斜杠爬坡,反斜杠下坡。

寻找知识的源头,处理书中没有的知识。找到语言规范,使用Ctrl+F,寻找for循环。

  基本数据类型的自动装箱与拆箱

将基本数据类型包装成引用数据类型再赋给引用变量,例如,Integer i = 4;

将引用数据类型拆箱变成基本数据类型,例如,System.out.println(i+10);

基本类型的整数,自动装箱成Integer对象,如果是1个字节,即-128到127之间,会放到缓存池中,使用时直接从缓存池中取出使用,节省了内存空间。因为数字很小且很少变化,使用很频繁,所以用一个数字对应多个对象。所以同一个数字装箱成的对象一样,==和equals都返回true。这是“享元”设计模式flyweight。

享元模式:很多很小的对象,他们有很多相同的东西,可以将他们变成一个对象,不同的东西变成外部的属性,作为函数的参数传入,称为外部状态。例如,String类、文件图标,对象只有一个,但名称不同。超过1个字节,使用频率低,一个数字对应一个对象,即使数字相同,但对象不同,所以==返回false,equals返回true。

  二、关键字:枚举、反射、Class类、反射、Constructor类、Field类、Method类、数组的反射、框架、类加载器、配置文件

枚举enum
定义一个类型,让该类的变量的取值只能是若干个固定值中的一个,否则编译报错,这个过程称为枚举。枚举可以让编译器在编译时就控制源程序中的非法值,普通变量无法实现。本质是限制了对象的创建。
普通类实现枚举:本类中,私有化构造函数,并限制了对象和引用变量的创建,将引用变量变成常量(对象类型)。外部类在使用本类时,引用变量只能赋值这些常量,不能指定规定以外的值,例如一星期的七天,3色的交通灯。
普通类实现枚举的思路:
1.       私有构造方法。
2.       每个元素分别用一个共有的静态变量表示。
3.       可以有共有方法和抽象方法。例如,采用抽象方法、匿名内部类和多态定义nextDay()就将大量的if、else转成成一个个独立的子类。
通过点击左边框里的错误图标,可以直接创建新的类、接口等,非常便捷。
    枚举相当于一个类,枚举里面的元素相当于常量,指向实例对象。可以调用一些方法对枚举的对象进行操作,就像普通类通过继承Object类获得了一些基本方法。例如,name()、toString()、ordinal()(自己的顺序)、getclass()。枚举也有一些静态方法,valueOf(),将字符串变成该枚举的对象;values(),将枚举里的元素装入一个数组中。
    枚举的对象获得:类.元素,例如,TrafficLamp.RED。
    枚举的构造方法特点:
1.       构造方法必须位于元素列表之后。成员变量和成员方法都要放在元素后面,否则报错。
2.       私有化构造方法。
3.       当枚举被调用,内部的元素(静态变量)会都被加载进内存,然后逐个调用空参数的构造函数创建相应的对象。
4.       调用带参数的构造函数创建对象:在元素后面加上括号,括号内传入构造函数的参数。例如,SUN(1)。由此可见,如果括号内没有参数,调用空参数构造函数。
访问修饰符:外部类:public和默认。内部类:publicprivate、protected和默认,和成员函数平级,访问修饰符也一样。
    匿名内部类的另类用法:{}及其里面的内容放在引用的后面,而不是对象的后面,例子如下。再者就是,子类对象可以调用父类的带参数的构造方法,例如,new data(30){};
    当枚举只有一个成员是,可以当做一个单例的实现方式,比单例设计模式方便,只要列出一个元素即可,其他方法一样。
    public enum TrafficLamp {//带有抽象的方法,但不用写出。
       RED(30){//另类的匿名内部类使用方法。
           public TrafficLamp nextLamp(){
              return GREEN;
           }  
       },
       GREEN(5){
           public TrafficLamp nextLamp(){
              return YELLOW;
           }  
       },
       YELLOW(40){
           public TrafficLamp nextLamp(){
              return RED;
           }  
       };
      
       public abstract TrafficLamp nextLamp();
       private int time;
       private TrafficLamp(int time){this.time = time;}
    }
   
反射(Reflect)与Class类
作用在于,在不知道类的具体内容(API或源代码)的情况下(只有.class文件)操作类的内容,也就是在类被编译后,再对类进行操作,操作的内存里的字节码。例如,给出参数,选择一个构造函数创造对象,这就需要调用反射来获得类的构造函数;获得对象中指定的变量的值;有对象和参数,调用某个方法。正因为此,需要抛出异常,因为这些成员可能没有。
JDK1.2出现,不是新特性。
Class类《——》java类
Constructor类《——》构造函数
Field类《——》成员变量
Method类《——》成员函数
Array《——》处理数组
 
Class类(java.lang包)
java类用于描述事物的特性。java程序中的任何java类和接口也属于同一种事物,描述这类事物使用Class类。对象——》java类和接口——》Class类。枚举是类,注释是接口。
通过Class中的各种方法,可以获得java类的信息,例如,类名、包名、父类等。但是没有构造函数,Class的实例对象就是java类在内存里的字节码,具有唯一性。例如,Class ch = Data.class;,对象为Data类在内存的字节码。.class文件不是字节码,只有被类加载器加载入内存后,内存里的二进制数据才是字节码。
通过forName()获得java类的字节码,例如,Class.forName(“java.lang.String”);获得String在内存的字节码。forName()获得字节码的情况有两种:
1. 如果该类已经在虚拟机,无需加载。直接找到字节码返回即可。
2. 如果该类不在虚拟机,使用类加载器,将类加载入虚拟机,然后从虚拟机中获得字节码。
java类的对象调用getClass()方法,获得对应的java类。例如,stud.getClass();。
获得该字节码对应的Class类对象的3种方式:
1. 类名.class,例如,System.class
2. 对象.getClass(),例如,new Data.getClass()//获得该对象的类的Class类对象
3. Class.forName(“类名’),静态方法,例如,Class.forName(“java.lang.String”);反射中使用比较多,类名也可以是变量,不用提前知道java类的名字。需要处理异常,可能类没有加载,但没有使用类加载器ClassLoader 。
通过上面三种方法都可以获得字节码,一个类在内存中只有唯一的一份字节码。
8个基本类型也有对应的Class对象。void也有对应的Class对象,例如,Class ch = void.class;。所以共有9个预定义的Class对象。
isPrimitive(),是否是基本数据类型。基本类型获得字节码,例如,int.class()。注意,基本类型的字节码和包装类的字节码不一样,例如,int.class 和Integer.class对应的字节码不一样。但是,Integer.TYPE表示包装类内部基本数据类型的字节码,所以int.class 和Integer.TYPE是一致的。9个预定义类型都是如此。
基本数据类型组成的数组,也有对应的字节码,是Class的实例对象,但不是基本类型。是数组类型,可以用isArray()判断是否是数组类型。
将鼠标放在错误图标上,可以看到错误原因。
总之,只要是在源程序中出现的类型,都有各自对应的Class实例对象。例如,数组等。
 
反射
反射就是将java类中的各种成分都解析成对应的java类(解构主义),例如,函数用Method类描述、包用Package、成员变量用Field、构造函数用Constructor。java类,纵向形成对象,横向的各种成分形成对应的java类。
具体的一个java类中的成分,用反射类的对象表示,例如,System类中有许多函数,exits()、out.println(),用Method类下不同的对象obj1、obj2表示,一个Method对象对应一个System类的函数。
一个类中的所有成员都可以用相应的反射API类的一个实例对象表示,通过调用Class类的方法获得这些对象,并对其进行操作。
 
Constructor类
描述类中的构造方法。通过调用Class中的方法获得Constructor对象。与java类相关。
获得构造函数:Constructor[] getConstructors();返回的是构造方法的数组。
Constructor<T> getConstructor(Class… type),传入想要的构造函数中的参数类型的Class类对象,获得该参数对应的一个构造函数。使用了可变参数,满足不同的参数需求。使用泛型,可以指定构造函数对象构造的类型。
用getConstructor返回的构造函数对象可以创建对应类的实例对象,使用newInstance()方法,例如
//获得String类的构造函数,该StringBuffer表示选择哪个构造方法。
Constructor con = String.class.getConstructor(StringBuffer.class);
//必须传入指定的参数(StringBuffer对象),而且需要转换类型。
String str =(String)con.newInstance(new StringBuffer("kkk"));
注意:newInstance()返回的是泛型,需要在Constructor处指定类型或者创建号对象后类型转换,例如,Constructor<String> con = String.class.getConstructor(StringBuffer.class);
编译时和运行时对程序的处理不同:编译只负责检查语法错误,并不执行等号右边的语句,也不明确变量的类型,所以需要类型转换或者指定泛型。树立编译和运行的两阶段思想。
需要注意,获得构造函数和用构造函数创建对象,内部使用的参数需要统一,不能上面用StringBuffer,下面用String,一种构造函数只能使用一种参数。
通过反射创建实例对象的步骤:
1、class字节码—》Constructor构造函数对象—》newInstance()创建对象。
2、class字节码—》(Class类的)newInstance()创建对象。
Class类的newInstance()方法,用于创建空参数的实例对象。底层仍然是调用Constructor类的newInstance()方法,使用缓冲机制保存实例对象,等到使用时提供出去,非常占用资源。
String str1 = String.class.newInstance();//已经知道类型,无需转换。
String str2 = (String)Class.forName("java.lang.String").newInstance();//需要转换类型。
Field类
代表java类中的成员变量。
右键—》Source—》使用构造函数目标创建构造函数。
ReflectPoint rf = new ReflectPoint(3,6);
Field fy = ReflectPoint.class.getField("y");//fy是ReflectPoint类的共有的成员变量y
System.out.println(fy.getInt(rf));//想要获得fy对应值,需要指定具体的ReflectPoint对象。
获得变量:Class类中的getField()只能获得共有的变量,getFields()返回变量的数组,getDeclaredField(),返回所有的变量,包括私有。
Field类中的get()方法获得变量对应的值,返回Object类型,需要转换。对于私有变量,还需要设置“暴力反射”才能获得对应值,例如,fx.setAccessible(true);。也有一些可以获得基本类型的值的方法,但是必须预先知道变量的类型才能使用,例如,getInt()。总之,值是和对象有关的。
字节码之间的比较应该使用==,能够确定是否是同一份字节码,equals会比较内容,不够严谨。
set(Object obj, Object value),将某个对象的某个成员变量的值换成新值。
getType(),获得某个变量的类型,注意返回Class对象。
getName(),获得某个变量的变量名。
变量的组成:类型、变量名和值,对应的获得方法:getType()、getName()和get()。这些方法都需要抛出异常。
 
Method类
代表类中成员方法。与java类相关,与对象无关。先获得方法,再用对象调用。
需要导包:import java.lang.reflect.*;。
获得java类中方法:使用Class类中的方法,
1、getMethod(String name, Class... parameterTypes),指定方法名和参数列表,获得方法。参数类型使用Class类。例如,int.class
2、getMethods(),获得方法组成的数组,Method[]。
对象调用方法:invoke(Object obj, Object... args),某个对象调用该方法,并传入参数,符合面向对象的思想。参数为可变参数,因为不同的方法对应不同的参数,可以使用基本类型。返回Object类型,需要转换。例如,Character ch = (Character)methodCharAt.invoke(s1, 2);相当于,s1.charAt(2)。如果s1是null,意味着这个Method对象对应的是静态方法。
JDK1.4时没有可变参数,传入数组,例如,Character ch = (Character)meCharAt.invoke(s1, new Object[]{2});。将数组打开,获得里面的元素作为参数,这个数组可以放入各种对象,例如,Integer、String等,使用多态。JDK1.5兼容了1.4的方法,会自动拆包数组。
专家模式:谁拥有数据,谁就可以调用方法。
 
用反射执行main主函数
可以直接用类调用主函数,例如,TestArguments.main(new String[]{});。写程序时,并不知道需要调用哪个类的main函数,等程序运行后在告诉调用哪个类的main,相当于主函数传入参数。先调用类,即使这个类没有被写好,只要运行时写好就可以。
需要给本类传入参数,右键—》Run As—》Arguments—》Program arguments,将外面类放入,即主函数传入参数。
String classname = args[0];
Method mainMethod = Class.forName(classname).getMethod("main", String[].class);
mainMethod.invoke(null,(Object)new String[]{"kk","qq"});//
JDK1.5为了兼容JDK1.4,如果传入一个数组,会默认为Object数组,自动拆包,将数组中的元素作为参数。将上例的数组拆包成2个字符串对象。为了防止这种情况,处理方法有二种:
1、将数组变成一个Object数组对象,例如,new Object[]{(new String[]{"kk","qq"})}。因为数组也属于Object类的子类。
2、转换数组的类型,例如,(Object)new String[]{"kk","qq"}。
 
数组的反射
相同的维度和数据类型的数组,其Class对象一样。
String getName(),返回类名,即该Class对象对应的类、接口、数组类、基本类型的名称。如果是数组,会按照数组给出简写字母。例如,int[]数组的Class对象的名字是[I,[表示数组,I表示int类型。
int[] a1 = new int[]{2,5,7};
    int[] a2 = new int[4];
    int[][] a3 = new int[3][5];
String[] a4 = new String[]{"d","j","f"};
Class getSuperclass(),获得该Class对象的父类的Class对象。
所有一维数组的父类是Object,二维数组可以看做Object[]。也就是说,整个一维数组、二维数组中的数组都可以看做Object类型。例如,a1、a3的元素、a4的元素都是Object。另外,引用数据的数组既可以当做Object,也可以当做Object[]。
Arrays的asList()方法在处理基本类型数组(int[])和引用类型数组(String[])时的差异:
可以将引用数据的数组变成集合,数组中的元素变成集合中的元素;会将整个基本数据的数组变成一个Object对象。因为JDK1.4中,asList()接受的是数组Object[];JDK1.5中,asList()接受的是可变参数T…a,T可以是基本型或引用型。例如,a4按照1.4处理,直接打印;a1按照1.5处理,打印出哈希值。想要打印基本数据的数组,可以逐个元素录入,或者
使用反射中的Array工具类对数组进行反射操作。Class中拥有Arrays没有的方法,例如判断对象所属的类是否是数组等,再用Array中的方法操作数组。这些方法基本是静态方法,需要将数组对象传入。例如,打印对象,类似拆包
public static void printObject(Object obj){
       Class oss = obj.getClass();//变成Class对象进行判断。
       if(oss.isArray()){
           int len = Array.getLength(obj);
           for(int i=0; i<len; i++){
              System.out.println(Array.get(obj, i));
       }
       }else{
           System.out.println(obj);
       }
}
目前还无法通过数组中的元素获得数组的类型,因为基本数据无法使用getClass()方法,引用数据可以使用该方法。因为getClass()方法属于Object类。通过getClass().getName()获得该元素的类型。考虑到多态,无法确定数组的类型。
 
    几种集合的比较
    ArrayList,内部存入的是对象的引用,不是对象本身,所以同一个对象可以放入多次。有索引,按照顺序排列。
HashSet,存入的也是对象的引用。先判断该对象是否存在,如果已经存在就不放入;不存在的话,再通过哈希值排序后存入。要替换已有的相同对象,必须先删除原有的对象,再存入新对象。
按照hashCode()和equals()方法进行比较。将HashSet中的元素按照哈希值分成若干区域,先将对象按照哈希值放入对应的区域,再在区域内使用哈希值和equals()排序。这种算法只在HashSet集合中才有效。只有哈希值和equals()都相等,才能是同一个元素。在其他集合中,不需要按照哈希值排序。
当一个对象被存入HashSet集合后,就不能修改对象中参与计算哈希值的变量了。对于元素的操作,都是在区域中完成,如果元素的哈希值变化,会进入其他区域,也就无法操作,例如remove()、contains()。
内存泄露:对于某些不用的数据,虽然代码上已经被释放,但实际内存里并没有释放,一直在运行。或者调用了系统底层的资源,但是没有关闭资源。日积月累,内存会被消耗殆尽。例如,在HashSet集合中,由于修改了元素的哈希值,导致无法对元素进行操作,尽管代码上的确进行了操作。
 
反射的作用——实现框架功能
先建框架,然后再写类。运行时,框架调用这些类。
使用别人的类的方式有两种:
1、直接调用别人的类。
2、通过反射,让别人的类调用我的类。
自己的类是工具,别人的类是框架。通过工具类,使得框架合乎自己的需求。例如,框架是房子,我的类是门、窗户等配件。
框架解决的核心问题:写框架时,还不知道需要调用什么类,需要使用反射Class.forName(“类名”),等到类写好后,框架获得类名即可使用类、操作类的对象。
类的名字一般放在配置文件中。创建一个File文件config.properties作为配置文件,设置className=类名,需要时可以通过更改配置文件,将框架改造成自己需要的样子。配置文件放在工程目录下。举例,写个小框架
InputStream ips = new FileInputStream("config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String classname = props.getProperty("className"); 
Collection collections = (Collection)Class.forName(classname).newInstance();
通过配置文件创建想要的对象,更改classname对应的类,即可获得其他想要的对象,例如,HashSet对象。
 
类加载器
得到配置文件的方式:
1、一定要使用完整的路径,但完整的路径不是固定的,是利用getRealPath()方法运算获得。项目的路径不固定,但配置文件在项目内的路径是固定的,例如,金山词霸\配置文件,金山词霸安装位置不固定,但配置文件的目录是固定的。
这种方式还可以保存对配置文件的更改,使用更广泛。
2、类加载器(ClassLoader)会将.class文件和配置文件都加载进内存,所以可以使用类加载器加载指定的配置文件。编写代码时,将配置文件放在classpath目录下,Eclipse放在src目录或子目录下;编译后,Eclipse会自动将所有源代码编译后的.class文件存放到classpath目录或bin目录下,并将非.class文件原样复制到这些目录下。例如,
InputStream ips =
               ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");//从根目录开始向下找配置文件。
    但这种方式只读,不能改,有局限性。SSH使用的都是类加载器。
    Class内部提供了直接获得配置文件的方法,跳过了类加载器。但底层仍然是在调用类加载器。默认在该.class文件的包中,不是的话,使用绝对路径:自己指定包,或者使用根目录。例如,
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
 

三、关键字:内省、JavaBean、PropertyDescriptor类、Introspector类、BeanUtils工具包、注解、Rentention、Target、注解的基本属性和高级属性 

内省IntroSpector
JavaBean主要用于传递数据信息,其方法用于访问私有变量,且方法名符合某种规则。
    如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()、get()获得。
    内省主要是对JavaBean进行操作。JavaBean内部的方法要按照某种规则命名,例如void setAge(int age)、int getAge()。JavaBean可以作为普通类进行操作;普通类如果内部有set()、get()方法,也可以当做JavaBean使用。
    JavaBean的属性是通过get()和set()方法推断出来的,即去掉get、set后的字母,例如,属性为age,而不是成员变量,因为成员变量看不见。
获得属性名的规则:如果属性名的第二个字母是小写,则把第一个字母小写。例如,gettime—>time,setTime—>time,getCPU—>CPU。
    JavaBean处理的好处:
    1、JavaEE中许多地方需要使用JavaBean。
    2、JDK给JavaBean提供的API称为内省。
 
    PropertyDescriptor类
    PropertyDescriptor类表示JavaBean类通过存储器导出一个属性。主要方法:
    1、getPropertyType(),获得属性的Class对象。
    2、getReadMethod(),获得用于读取属性值的方法;getWriteMethod(),获得用于写入属性值的方法。
    3、hashCode(),获取对象的哈希值。
    4、setReadMethod(Method readMethod),设置用于读取属性值的方法;setWriteMethod(Method writeMethod),设置用于写入属性值的方法;
    导包java.bean.*;
    通过属性名获取对应的值,利用反射方法,如下:                                         
           ReflectPoint pt1 = new ReflectPoint(7,9);
              String propertyName = "x";//给一个属性,获取值
              PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
              Method methodGetX = pd.getReadMethod();//Read对应get()方法
              Object reValue = methodGetX.invoke(pt1);
    给某个属性设置值,如下:
           String propertyName2 = "y";//给一个属性,设置值
              PropertyDescriptor pd2 = new PropertyDescriptor(propertyName2,pt1.getClass());
              Method methodSetY = pd2.getWriteMethod();//Write对应set()方法
              methodSetY.invoke(pt1,3);
       右键—》Source—》Generate Geters and Setters,创建get()和set()方法。
    选择一些代码,右键—》Refactor—》Extract Method,创建一个方法,提高复用性。  
 
    Introspector类
    将JavaBean中的属性封装起来进行操作。在程序把一个类当做JavaBean来看,就是调用Introspector.getBeanInfo()方法,得到的BeanInfo对象封装了把这个类当做JavaBean看的结果信息,即属性的信息。需要导包java.beans.*。
    getPropertyDescriptors(),获得属性的描述,可以采用遍历BeanInfo的方法,来查找、设置类的属性。
    private static Object getProperty_2(Object pt1, String propertyName) throws Exception {
               BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
               PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
               Object reValue = null;
               for(PropertyDescriptor pd : pds){
                     if(pd.getName().equals(propertyName)){
                            Method methodGetX = pd.getReadMethod();
                            reValue = methodGetX.invoke(pt1);
                            break;
                     }
               }
               return reValue;
        }
    通过这两个类的比较可以看出,都是需要获得PropertyDescriptor,只是方式不一样:前者通过创建对象直接获得,后者需要遍历,所以使用PropertyDescriptor类更加方便。
   
    BeanUtils工具包              
    为JavaBean提供更多、放方便的功能。
    beanutils.jar = beanutils-core.jar + beanutils-bean-collections.jar,可以通过BuildPath,添加额外的jar包,或者工程下建立lib目录,将jar包复制进来,再加载这个jar包:右键—》add to BuildPath。使用时需要导包:org.apache.commons.beanutils.BeanUtils。
    需要配合使用acpche提供的日志包:logging。
    获得属性的值,例如,BeanUtils.getProperty(pt1,"x"),返回字符串
    设置属性的值,例如,BeanUtils.setProperty(pt1,"y",22),参数是字符串或基本类型自动包装。设置属性的值是字符串,获得的值也是字符串,不是基本类型。
    BeanUtils的特点:
1、对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成。
2、对引用数据类型的属性的操作:首先在类中必须有对象,不能是null,例如,private Date birthday=new Date();。操作的是对象的属性而不是整个对象,例如,BeanUtils.setProperty(pt1,"birthday.time",121);
Java7的新特性:Map和JavaBean之间可以进行相互转换,key是属性,value是值。
describe:JavaBean—>Map;populate:Map—>JavaBean。例如:
    Map map = (name:Kim,age:18);
        BeanUtils.setProperty(map,"name","Kim");
    copyProperties(Object dest, Object orig) ,将一个对象的属性值复制到另一个对象的属性,需要保证属性一致。
   
    PropertyUtils类
    和BeanUtils不同在于,运行getProperty、setProperty操作时,没有类型转换,使用属性的原有类型或者包装类。
 
    注解Annotation
    JDK1.5出现的新特性。在java.lang.annotation包中。
    对于过时的语句,java会提示过时了,通过@SuppressWarnings("Deprecation")在DOS中取消提示,但Eclipse无法取消。这就是注解,相当于标记。编译器、开发工具、javac通过反射获得注解里的内容,进而明确应该做什么、不应该做什么。注解可以加在包、类、属性、方法、参数及局部变量之上。
    一个注解就是一个类。@SuppressWarnings,取消警告。@Deprecated,已过时,老版可以用,新版无法用。
    HashSet集合中,对象必须覆盖Object类的equals()方法,否则会继续使用Object类的equals()方法进行比较,错误的比较方法。覆盖equals()方法,参数必须一致,为了防止错误写入本类的对象,加入@Override,必须正确覆盖父类方法,不是创建新方法。
   
    注解的应用
    在源程序中,调用一个类,这个类会用到注解,需要先准备好注解类,类在调用注解类的对象。注解类的写法类似接口,@interface。先写好注解类A,将注解放在类B中,类C在调用类B时通过反射获得注解类A的内容,进而明确该做什么、不该做什么。可以加上多个注解,加上的实际是注解类的对象:@interfaceA。
    main()方法必须放在一个类下,但与这个类不一定有所属关系。
    在注解类A上加注解B,这个注解B只为这个注解类A服务,B称为“元注解”。类似的还有元信息、元数据。元注解有2个:Rentention和Target。对注解类的注解,可以理解为注解类的属性。
   
 
    Rentention注解类
    注解的生命周期:Java源文件—》class文件—》内存中的字节码。编译或者运行时,都有可能会取消注解。Rentention的3种取值意味让注解保留到哪个阶段,RententionPolicy.SOURCE、RententionPolicy.CLASS(默认值)、RententionPolicy.RUNTIME。
    @Override、@SuppressWarnings是默认保留到SOURCE阶段;@Deprecated是保留到RUNTIME阶段。
    Rentention相当于注解类的一个属性,因为Rentention的值不同,注解类保留到的阶段不同。注解类内部Rentention的值使用value表示,例如,@Deprecated中,value=Runtime。
    Rentention的值是枚举RententionPolicy的值,只有3个:SOURCE、CLASS、RUNTIME。
 
    Target注解类
    性质和Rentention一样,都是注解类的属性,表示注解类应该在什么位置,对那一块的数据有效。例如,@Target(ElementType.METHOD)
Target内部的值使用枚举ElementType表示,表示的主要位置有:注解、构造方法、属性、局部变量、函数、包、参数和类(默认值)。多个位置使用数组,例如,@Target({ElementType.METHOD,ElementType.TYPE})。
类、接口、枚举、注解这一类事物用TYPE表示,Class的父类,JDK1.5的新特性。
 
注解的基本属性
属性,给注解提供更加详细的信息。
注解相当于接口,属性相当于方法。例如,@ItcastAnnotation(color="black"),给属性赋值,取值时类似调用方法,例如,System.out.println(annotation.color());。所有的属性必须全部出现,除非有缺省值。
如果只有value属性,没有其他属性,可以不写=,只针对value,例如,@SuppressWarnings("Deprecation")。或者有其他属性而且有缺省值,例如,String color() default "blue";,此时value单独出现,不用=。
获得注解的属性的值,例如
if(AnnotationDemo.class.isAnnotationPresent(ItcastAnnotation.class)){
        ItcastAnnotation annotation =
              (ItcastAnnotation)AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);
        System.out.println(annotation.color());
        }
}
利用反射获得注解的对象,在让该对象调用属性对应的方法。注意类型转换。
Rentention和Target也是属性,都是value对应的值,值的类型分别是RententionPolicy和ElementType,例如,@Retention(value=RetentionPolicy.RUNTIME)。
 
注解的高级属性
给注解增加高级属性,数组、枚举、注解。
数组类型的属性
例如,int[] arr() default {3,7,5};,MyAnnotation(arr={3,7,6})。如果数组只有1个元素,可以不加{}。@Target({ElementType.METHOD,ElementType.TYPE})也是数组类型的属性。
枚举类型的属性
//注解类内部的内容
EnumerationDemo.TrafficLamp lamp() default EnumerationDemo.TrafficLamp.RED;
//调用注解的类上
@ItcastAnnotation(lamp=EnumerationDemo.TrafficLamp.YELLOW)
//对注解进行操作
System.out.println(annotation.lamp().nextLamp().name());
注解类型的属性
将一个注解类作为属性加入到另一个注解类中。
MetaAnnotation annotationAtt() default @MetaAnnotation("Jobs")
@ItcastAnnotation(annotationAtt=@MetaAnnotation("Kim"))
annotation.annotationAtt().value()
注解的返回值可以是8个基本类型、String、Class、枚举以及前面类型的数组,内部还有属性。
需要详细学习注解,可

 转自

原文地址:https://www.cnblogs.com/lbangel/p/3523504.html