------------android培训、java培训、期待与您交流!
-------------
Eclipse的使用技巧
eclipse与myeclipse的关系(都是java开发的):
后者是前者的一个插件,后来为了方便使用,myeclipse集合了eclipse,后者是收费的。
可大部分人都是用 eclipse 等常用的免费软件。最好用的因为别人都在用。这就是市场。
1、eclipse是java的一种IDE开发工具。
请注意一些英文简写的含义,对应的每个单词。平时积累!
2、什么叫一个工程(project):
我的看法是,一个独立的项目就是一个工程,一个工程包含多个源文件,运行环境,需要的资源。
IDE(Integrated Drive Electronics)开发工具都支持使用工程化方式管理一个项目的程序开发过程,一般来说一个相对独立的项目就是一个工程,一个项目中涉及的多个java文件,资源文件等用一个工程进行管理。(在这里可以看看以前工作间中的某个工程的结构),在不使用工程管理的情况下,如果一个项目中包括多个Java源文件,编程人员需要精心维护这些源文件之间、以及源文件与其它文件的目录关系,需要逐一编译这些源文件,需要手工启动运行编译后的结果。如果将一个程序的所有源文件用一个工程来组织,开发工具能对所有源文件集中管理,记住每个源文件的位置和相互关系。 工程中有哪几个源文件、启动类是哪个、启动参数设置等配置信息在工程中都记录。
3、工作间(workspace):
一个工作间包含多个工程,工作间与工作间之间可以独立配置信息
(编译、运行的版本、快捷键之类的)
一个workspace可以包含多个project,一个workspace保留了eclipse的一套环境选项的配置,例如,所使用的javac和java命令,等等,细节请查看window->preferences。如果要为eclispe再配置一套环境选项,可以再创 建一个workspace。Package explorer视图窗口中的filters菜单项,可以显示空的父包(此功能默认是关闭的)。
4、设置快捷键、工作间。
快捷键使用技巧(经常出现软件之间快捷键冲突):
快捷键的位置:window->preferences->General->keys,设置alt+/键进行内容提示时,要注意解除alt+/键原来的绑定关系,直接输入alt+/就可以找到它的绑定关系,删除绑定关系时也可以使用remove binding这个按钮,课后必须教会大家在eclipse中。
代码模板的设置位置:java->editor->Templates
代码模板(template)的作用
自己可以添加,自定义,例如设置tryf 方便敲出try{}finally{};
多想一想:eclipse工作台中的所有工程继承工作台的配置,其中某个工程也可以覆盖工作台的配置!这是不是java面向对象的思想啊?
张老师的课多有意思!
设置单个工程的javac和java
选择工程,右键->properties可以设置javac,右键->run asàopen run dialog可以设置java。
张老师的演示:
先用新的工作间,然后创建新工程,默认的语言即为5.0。先使用Integer x = 3;调整编译器的语法版本为1.4,看到eclipse窗口报错了。然后将这个工程的语言设置为6.0,马上又看到bad version .class运行错误了,这是因为myeclise自带的java为1.5 。然后再将整个工作间的javac设置为6.0(eclipse自带的是jdk1.5),然后看新建工程的javac,也随之改成了6.0,运行则又报bad version .class错误。将工程的编译语言再单独改为5.0,运行则没了问题。整个工作间的语言设置为6.0后,再将整个工作间的java也设置为自己安装的java6。
5、Perspective与view(透视图与视图)
1)所谓的不同的透视图:不同的小窗口(view)构成的大窗口(perspective),便于进行特有的操作,通过window的b按钮选择不同的view。
2)在eclipse中怎么调试?先双击最左边设置断点,然后回到代码区点右键出现debug,
出现的debug视图后选择需要调试的数据进行watch。
6、关于工作间配置javac(编译版本)和java(运行版本),简单来说就是高版本运行同级或低版本编译的源文件。向下兼容。
7、怎么导入工程
找到需要导入的工程,然后粘贴到所需要的工作台,import导入(看是否需要设置jre),buildpath中设置。
静态导入(1.5特性,好处是不用写类名)
import语句可以导入一个类或某个包中的所有类
import static语句导入一个类中的某个静态方法或所有静态方法
语法举例:
import static java.lang.Math.sin;
import static java.lang.Math.*;
可变参数
是为了解决一个简单的方法在参数不同时多个重载的问题,用带可变参数的一个函数就能解决。
可变参数的特点:
1)只能出现在参数列表的最后;
2) ... 位于变量类型和变量名之间,前后有无空格都可以;
3)调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组 的形式访问可变参数。
Public int add(int x, int... args){//也可以直接(int..args)就是说传不传都可以
Int sum = x;
For(int i = 0; i<=args.lengrth;i++){
Sum+=args[i];
}
return sum;
}
增强for循环
语法:
for ( type 变量名:集合变量名 ) { … }
注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
举例:
public static int add(int x,int ...args) {
int sum = x;
for(int arg:args) {
sum += arg;
}
return sum;
}
基本数据类型的自动拆箱与装箱
——> -128~127之间的特殊性。为什么要这样设计,好处?
——> 享元模式(Flyweight Pattern):享元模式的特点是,复用我们内存中已存在的对象,降低系统创建对象实例。
自动装箱:
Integer num1 = 12;
自动拆箱:
System.out.println(num1 + 12);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12;
System.out.println(num1 == num2);
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);//false
枚举
为什么要有枚举?
问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。
1、私 有的构造方法。
2、 每个元素分别用一个公有的静态成员变量表示。
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类
public abstract class WeekDay1 { private WeekDay1(){} public final static WeekDay1 SUN = new WeekDay1(){ @Override public WeekDay1 nextDay() { / / TODO Auto-generated method stub return MON; } }; public final static WeekDay1 MON = new WeekDay1(){ @Override public WeekDay1 nextDay() { // TODO Auto-generated method stub return SUN; } }; public abstract WeekDay1 nextDay(); public String toString(){ return this==SUN?"SUN":"MON"; } }
枚举的基本应用
举例:定义一个Weekday的枚举。
扩展:枚举类的values,valueOf,name,toString,ordinal等方法(记住,讲课时要先于自定义方法前介绍,讲课更流畅)
总结:枚举是一种特殊的
String |
name() 返回此枚举常量的名称,在其枚举声明中对其进行声明。 |
|
int |
ordinal() 返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 |
|
String |
toString() 返回枚举常量的名称,它包含在声明中。 |
|
static
|
valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。 |
import java.util.Date; public class EnumTest { public static void main(String[] args) { // TODO Auto-generated method stub WeekDay1 weekDay = WeekDay1.MON; System.out.println(weekDay.nextDay()); WeekDay weekDay2 = WeekDay.FRI; System.out.println(weekDay2); System.out.println(weekDay2.name()); System.out.println(weekDay2.ordinal()); System.out.println(WeekDay.valueOf("SUN").toString()); System.out.println(WeekDay.values().length); /*SUN second first FRI FRI 5 SUN 7*/ new Date(300){};} 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");} } /*枚举类,交通灯*/ public enum TrafficLamp{ RED(30){public TrafficLamp nextLamp(){return GREEN;}}, GREEN(45){public TrafficLamp nextLamp(){return YELLOW;}}, YELLOW(5){public TrafficLamp nextLamp(){return RED;} }; public abstract TrafficLamp nextLamp(); private int time; private TrafficLamp(int time){ this.time = time;} } }
反射
对比提问: Person类代表人,它的实例对象就是张三、李四这样一个个具体的人, Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。对比提问:众多的人用一个什么类表示?众多的Java类用一个什么类表示?
人àPerson
J ava类àClass
Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date");
九个预定义Class实例对象:........
参看Class.isPrimitive方法的帮助
Int.class == Integer.TYPE
数组类型的Class实例对象
Class.isArray()//判断字节码是否是数组对象
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…
1.Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别哦。Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。学习反射,首先就要明白Class这个类
/*Person p1 = new Person("zhangsan");
Person p2 = new Person("lisi");
*/
/*Class x1 = Vector类在内存里的字节码
Class x2 = Date类在内存里的字节码*/
Class x1 = Vector.class;
Class x2 = Date.class;
每个java类都是Class的一个实例对象,它们的内容不同,但是,它们的特征相同,譬如,都有方法,有字段,有父类,有包。
3.一个奇怪的问题:加载了字节码,并调用了其getMethods之类的方法,但是没有看到类的静态代码块被执行,只有在第一个实例对象被创建时,这个静态代码才会被执行。准确的说,静态代码块不是在类加载时被调用的,而是第一个实例对象被创建时才执行的。
int.class,(int []).class
int [] ints = new int[0];
ints.getClass();
Date d1 = new Date(); Class clazz1 = d1.getClass(); Class clazz2 = Date.class; Class clazz3 = null; clazz3 = Class.forName("java.util.Date"); if(clazz1==clazz2) { System.out.println(clazz1.getName()); } if(clazz1==clazz3){System.out.println(clazz1.getName());}
反射就是把Java类中的各种成分映射成相应的java类.
例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量、方法、构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的属性、方法、构造方法、修饰符、包、注解等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Constructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
Constructor类
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
2. 一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?根据参数的个数和类型,例如,
示例代码: ReflectPoint point = new ReflectPoint(1,7); Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y"); System.out.println(y.get(point)); //Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x"); Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x"); x.setAccessible(true); System.out.println(x.get(point)); 作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。 ReflectPoint类的定义: public class ReflectPoint { private int x; public int y; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } } 先用getField方法获取变量x时报错,用完getDeclardField方法后还是报错,但两者的错误不一样 --------------------------------------------------------------- 作业参考答案: class Xxx { String name="abc"; String email="abd"; int x = 5; } func(Object obj) { Field [] fields = obj.getClass().getDeclaredFields();//获取obj中所有成员变量 for(Field field : fields)//遍历 { if(field.getType()==java.lang.String.class) //这里用==优于equals,因为对应字节码只有一个 { field.setAccesible(true); String original = (String)field.get(obj); field.set(obj,original.replaceAll("b","a"); } } }
一个问题,我把自己的变量定义成private,就是不想让人家访问,可是,现在人家用暴力反射还是能够访问我,这说不通啊,能不能让人家用暴力反射也访问不了我。首先,private主要是给javac编译器看的,希望在写程序的时候,在源代码中不要访问我,是帮组程序员实现高内聚、低耦合的一种策略。你这个程序员不领情,非要去访问,那我拦不住你,由你去吧。同样的道理,泛型集合在编译时可以帮助我们限定元素的内容,这是人家提供的好处,而你非不想要这个好处,怎么办?绕过编译器,就可以往集合中存入另外类型了。
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));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该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})形式。
大家应通过思考和推理的方式来学习反射中的API,例如,Class.getMethod方法用于得到一个方法,该方法要接受什么参数呢?显然要一个方法名,而一个同名的方法有多个重载形式,用什么方式可以区分清楚想得到重载方法系列中的哪个方法呢?根据参数的个数和类型,例如,Class.getMethod(name,Class... args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表。
再强调一遍参数类型用什么来表示啊?用Class对象!
用反射方式执行某个类中的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,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
我给你的数组,你不会当作参数,而是把其中的内容当作参数。
Class clazz = Class.forName(arg[0]);
Method mMain = clazz.getMethod("main", String[].class);
mMain.invoke(null,new Object[]{new String[]{"aaa","bbb"}});
mMain.invoke(null,(Object)new String[]{"aaa","bbb"});
class TestArrayArguments {
public static void main(String [] args){
for(String arg:args){
System.out.println("----------" + arg + "----------");
}
}
}
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(编译期就有提示)。
反射的作用à实现框架功能
框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。框架要解决的核心问题.我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
综合案例:
先直接用new 语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成 ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。
然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异。
引入了elipse对资源文件的管理方式的讲解。
什么是框架?例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
你 做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。程序中不处理异常,而是main方法声明抛出异常,便于大家可以集中看主要的关键代码。
Class类也提供getResourceAsStream方法的比喻:如果你每次都找我给你商店买可乐,那我还不如直接向你买可乐,即直接提供一个买可乐的方法给你。
int[] a = new int[3];
Object obj = a;
//Object[] obj1 = a //有错!
Object[] obj3 = a1
Object obj4 = a3;
if(obj4 instanceof String[][]){
System.out.println(obj1.getClass().isArray());
}
**在这里分析研究Arrays.asList()方法处理int[]和String[]时的差异,以及Arrays.deepToString()方法不能处理int[],但能处理String[]的原因。
4.
private static void printObject(Object obj) {
if(obj.getClass().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);
}
}
5.似乎没有办法直接得到,需要取出每个元素对象,然后再对各个对象进行判断,因为其中每个具体元素的类型都可以不同,例如Object[] x = new Object[]{“abc”,Integer.Max}。Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能调用public static void main(String [] arg
}
举例应用
public static void main(String[] args) throws Exception{
//应该先直接用ArrayList和HashSet,然后才引入从配置文件读,这样便于学员学习。
Properties props = new Properties();
//先演示相对路径的问题
//InputStream ips = new FileInputStream("config.properties");
/*一个类加载器能加载.class文件,那它当然也能加载classpath环境下的其他文件,既然它有如此能力,它没有理由不
顺带提供这样一个方法。它也只能加载classpath环境下的那些文件。注意:直接使用类加载器时,不能以/打头。*/
//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/javaenhance/config.properties");
//Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/javaenhance/config.properties");
props.load(ips);
Ips.close();
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能调用public static void main(String [] args)
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。
思考题:怎么得到数组中的元素类型?
Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]
1.
看Class类帮助文档开始部分的介绍。
String[] a1 = new String[8];
String[] a2 = new String[9];
String[][] a3 = new String[6][4];
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass() == a3.getClass());
2.
看getSuperClass方法的帮助
System.out.println(a1.getClass().getSuperclass().getName());
String className = props.getProperty("className");
Class clazz = Class.forName(className);
Collection collection = (Collection)clazz.newInstance();
//Collection collection = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(5,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collection.add(pt1);
collection.add(pt3);
collection.add(pt1);
System.out.println(collection.size());