Java功力,由基础体现。

 

Eclipse的使用技巧

Workspace 与project 切换工作间与导入项目:一个工作间自带一套eclipse配置变量。常用快捷键:

1、显示系统提示:ALT+/(最常用:syso+alt+/ 补全代码)   Ctrl+D:删除
2、程序代码自动排版:Ctrl+Shift+F(jsp文件是全部重排,java文件是可以对选定的代码重排)
3、自动汇入所需要的类别:Ctrl+Shift+O(注意和Ctrl+Shift+M区别)
4、查看使用类别的原始码:Ctrl+鼠标左键点击(链接进入)
5、将选取的文字批注起来:Ctrl+/(后期很少用)
6、将选取的文字取消注解:Ctrl+ \
7、视景切换:Ctrl+F8
8、保存所有文件:Ctrl+Shift+S
9、关闭所有文件:Ctrl+Shift+F4
10、跳转到指定行:Ctrl+L(调试的时候很管用)
11、查找下一个:Ctrl+K
12、列出当前文件的结构:Ctrl+F3/Ctrl+O
13、列出类的继承结构:Ctrl+T(对于熟悉陌生的系统类很有用)
14、方法或类的原始码:F3
15、方法做在类的方法结构:F4
16、复制选定的代码:Ctrl+Alt+(箭头)   移动当前行:Alt+(箭头)
17、当前行插入一行:Ctrl+Shift+Enter
18、将选定字母全部转换成小写字母:Ctrl+Shift+Y
19、将选定字母全部转换成大写字母:Ctrl+Shift+X
20、将选定的换成连接:Ctrl
21、搜索所有文件中,包含的关键字:Ctrl+H(很管用)
22、系统辅助或者代码提示及补充:alt+/
23、找另外一半括号:ctrl+shift+p
24、重新整理导入的包(只导入需要的包,不需要的会自动去掉):ctrl+shift+m(注意和Ctrl+Shift+O区分)
25、编辑框等窗口最大化:Ctrl + m
26、编辑器的回退,前进,切换:Alt + 左右箭头,Ctrl + F6
27. 重构:Ctrl+Shift+R

Perspective与view:一个Perspective(透视图)代表了若干个view的集合,设置单个工程的javac和java,选择工程,右键->properties可以设置javac,右键->run as a open run dialog可以设置java运行参数.

 

静态导入

静态导入(static import)[08]

      import语句可以导入一个类或某个包中的所有类。

      importstatic语句导入一个类中的某个静态方法或所有静态方法。

语法举例:

      import staticjava.lang.Math.sin;

      import staticjava.lang.Math.*;

 

可变参数

问题:一个方法接受的参数个数不固定,例如:

      System.out.println(add(2,3,5));

      System.out.println(add(1,2,3,5));

可变参数的特点:

只能出现在参数列表的最后;这个要记住!!

“...”位于变量类型和变量名之间,前后有无空格都可以;

隐含创建一个数组,在方法体中以数组的形式访问可变参数。以数组的形式来用。

package cn.itcast.day1;
public class VarableParameter {
   public static void main(String[] args) {
      System.out.println(add(2,3));
      System.out.println(add(1,2,3));
   }  
   public static int add(int x,int ... args){                                                                                                        
      int sum = x ;      
      for (int i = 0; i < args.length; i++) {
         sum += args[i];
      }
      return sum;
   }
}

 

for循环增强

语法:

for(type 变量名:集合变量名 )

      { … }

注意事项:

      1、迭代变量必须在( )中定义!

      2、集合变量可以是数组或实现了Iterable接口的集合类。

以上程序采用增强for 循环的示例:

public static int add(int x,int ... args){
      int sum = x ;      
/*for (int i = 0; i < args.length; i++) {                                                                                                             
         sum += args[i];
      }*/
      for(int
         sum += arg;
      }
      return sum;
   }

 

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

 

自动装箱:Integer num1 = 12;

自动拆箱:System.out.println(num1 + 12);

基本数据类型的对象缓存:

Integer num1 = 12;  
      Integer num2 = 12;  
      System.out.println(num1 == num2);//true,这块相等,<=127都是真的。  
      Integer num3 = 129;                  
      Integer num4 = 129;  
      System.out.println(num3 == num4);//false,这块不相等,因为是对象。  
        
      Integer num5 = Integer.valueOf(12);  
      Integer num6 = Integer.valueOf(12);  
      System.out.println(num5 == num6);//true,这块的道理同上。  
      Integer num7 = Integer.valueOf(128);  
      Integer num8 = Integer.valueOf(128);  
      System.out.println(num5 == num6);//false,这块的道理同上。  
 
/*-128~127的数字(一个字节),一旦是Integer对象的话,缓存起来,再次使用时不新建。
*  享元模式:有很多小的对象,有很多相同的属性,变为一个对象,不同的属性作为方法的参数,作为外部状态,相同的属性称之为这个对象的内部状态,*/

享元设计模式(flyweight):当对象很小而又在很多地方都要用到时,就没有必要创建很多对象,而是使用固定的对象。

 

枚举

为什么要有枚举?

      问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。

      枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。

用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能

      1、私有的构造方法。

      2、每个元素分别用一个公有的静态成员变量表示

采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。

      总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。

 

枚举的高级应用

枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。

枚举元素列表必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。

带构造方法的枚举

      1、构造方法必须定义成私有的。

      2、如果有多个构造方法,该如何选择哪个构造方法?

传参数即可调用指定构造方法。

public enum WeekDay{     
      SUN(1),MON(),TUE,WED,THI,FRI,SAT;//元素列表必须放在首行。
      private WeekDay(){System.out.println("first");}//枚举的构造方法必须是私有的。                                    
      private WeekDay(int day){System.out.println("second");}    
   }

 

 

带方法的枚举

1、定义枚举TrafficLamp。

2、实现普通的next方法。

3、实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。

4、增加上表示时间的构造方法。

public enum TrafficLamp{
      RED(30){
         public TrafficLamp nextLamp(){
         returnGREEN;         
         }
      },
      GREEN(60){
         public TrafficLamp nextLamp(){                                                                                                              
         returnYELLOW;        
         }
      },
      YELLOW(5){
         public TrafficLamp nextLamp(){
         returnRED; 
         }
      };
      public abstract TrafficLamp nextLamp();
      
      private inttime;//私有成员变量
//构造函数传递时间
      private TrafficLamp(int time){this.time
   }
   
    new Date(300){};//new 子类的实例对象,可以调用父类的有参的构造方法。

 

枚举只有一个成员时,就可以作为一种单例的实现方式。

 

反射

反射的基石àClass类  [17]

      (Class也是一个类,代表的是一类事物?)

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。

对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?

对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。

      人-->Person

      Java类-->Class

Class类代表Java类,它的各个实例对象又分别对应什么呢?

      ①对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。

      ②一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?

 

如何得到各个字节码对应的实例对象(Class类型)

类名.class,例如,System.class;Person.class

对象.getClass(),例如,new Date().getClass()

      3、Class.forName("类名"),例如,Class.forName("java.util.Date");

forName()的作用:返还字节码

      1、虚拟机已经加载该字节码,直接放回。

      2、JVM还没有这份字节码,用类加载器去加载,加载并返回。

 

9个预定义Class实例对象(8种基本数据类型和void都有对应的Class对象):

参看Class.isPrimitive方法的帮助

Int.class == Integer.TYPE

数组类型的Class实例对象:Class.isArray()

 

"abc";     
      Class cls1 = str1.getClass();     
      Class cls2 = String.class;     
      Class cls3 = Class.forName("java.lang.String");//必须是完整的类名。
   
      System.out.println(cls1 == cls2);//true是一份字节码
      System.out.println(cls1 == cls3);//true
      
      System.out.println(cls1.isPrimitive());//false 不是基本类型字节码
      System.out.println(int.class.isPrimitive());//true 是基本类型字节码
      System.out.println(int.class == Integer.class);//false
      System.out.println(int.class == Integer.TYPE);//ture TYPE:代表是包装的基本类型的字节码。           
      System.out.println(int[].class.isPrimitive());//false 不是原始类型
      System.out.println(int[].class.isArray());//true

总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…

 

反射

反射就是把Java类中的各种成分映射成相应的java类。

      例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

      一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。

                              

 

Constructor类

Constructor类代表某个类中的一个构造方法。

 

得到某个类所有的构造方法:

Constructor [] constructors=Class.forName("java.lang.String").getConstructors();

 

得到某一个构造方法:

Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

//获得方法时要用到类型。

 

创建实例对象:[19]

通常方式:String str = new String(newStringBuffer("abc"));

反射方式:String str =(String)constructor.newInstance(new StringBuffer("abc"));

Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);
String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));

//调用获得的方法时要用到上面相同类型的实例对象。

 

Class.newInstance()方法:

例子:String obj =(String)Class.forName("java.lang.String").newInstance();

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。

 

---------------------------------------------------编译器只看变量的定义,不看代码的执行。

 

 

Field类

Field类代表某个类中的一个成员变量。(字段)

      问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。[20]

ReflectPoint pt1 = new ReflectPoint(3,5);
"y");
      System.out.println("filedY=="+fieldY);
//fieldY的值是多少?是5?错。
//fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。                                     
      System.out.println(fieldY.get(pt1));    
   
//获取私有(不可见)的变量,只要声明过的。------获取x的值
"x");
      fieldX.setAccessible(true);//私有暴力获取。暴力反射。
      System.out.println(fieldX.get(pt1));

 

private static void changeStringValue(Object obj) throws Exception {
//扫描所有String类型的变量
//得到所有的字段,-->得到所属的字节码,-->getFields 得到所有的字段
      for(Field field : fields){
 //if(field.getType().equals(String.class)){
 //字节码用 ==  进行比较---->因为是同一份字节码。用==比较
         if(field.getType() == String.class){
//取得字节码的值
'b', 'a');
//需要set设置,field内容改变。(设置字节码的值)                               
         }        
      }     
   }

 

 

 

Method类

Method类代表某个类中的一个成员方法。((类)字节码里面的方法)

得到类中的某一个方法:

例子:Method charAt = Class.forName("java.lang.String").getMethod("charAt",int.class);

调用方法:

      通常方式:System.out.println(str.charAt(1));

      反射方式:System.out.println(charAt.invoke(str,1)); 

//str1.charAt(1)
      Method methodCharAt = String.class.getMethod("charAt", int.class);
//用反射的方式,得到字节码的方法,在拿方法去作用于某个对象。(“方法名”,类型)                  
      System.out.println(methodCharAt.invoke(str1, 1));

 

      如果传递给Method对象的invoke()方法的第一个参数为null,[ invoke(null,1) ]这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!(静态方法调用不需要对象。)

jdk1.4和jdk1.5的invoke方法的区别:

      Jdk1.5:public Object invoke(Object obj,Object... args)

      Jdk1.4:public Object invoke(Object obj,Object[]args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。

 

 

用反射方式执行某个类中的main方法

目标:

去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?

问题:

      启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:

      mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});

      mainMethod.invoke(null,(Object)newString[]{"xxx"}); 编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。

      为什么要用反射方式调用?因为不知道类名。

 

 

数组的反射

1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。

3、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

4、Arrays.asList()方法处理int[]和String[]时的差异。接收的是Object[] obj数组 ------[24]

5、Array工具类用于完成对数组的反射操作。

private static void printObject(Object obj) {
      Class clazz = obj.getClass();
      if(clazz.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);
      }     
   }

 

HashCode()方法的作用:

HashCode值:由此对象的内存地址换算而来。

哈希算法:将集合分为若干个储存区域,每个对象可以算出一个哈希码,将哈希码分组,每组分别对应某个储存区域,根据一个对象的哈希码就能确定该对象储存在哪个区域。

HashSet:采用哈希算法的集合。实现了Collection接口,只能存入不同HashCode对象,即只存入不同的对象,如果希望存入具有相同内容的两个对象,则需覆盖对象的HashCode和 equals方法。

ArrayList:实现了Collection接口,对象之间有指定顺序,允许重复元素——即使是同一对象,也会被顺序存入。

当一个对象被存入HasthSet中后,就不能再修改这个对象中那些参与计算哈希值的字段了,否则,修改后的哈希值与最初存入HashSet的就不相符了,此时HashSet将无法检索到此对象,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄漏。

 

反射的作用:实现框架功能

 

·框架与框架要解决的核心问题  [28]

      我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

·框架要解决的核心问题

      我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?

      因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。

//注意一定要用完整的路径,但完整的路径不是硬编码。是运算出来的。
//InputStream ips = new FileInputStream("config.properties");
      
//类加载器
//先找到类,在用到类加载器方法。class的方法。
// InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
//指的从根目录下开始找。
//相对路径开始
      InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
            
      Properties props = new Properties();
      props.load(ips);
      ips.close();
      
"className");//key     

      Collection collections = (Collection)Class.forName(className).newInstance();