黑马程序员——19Java高新技术1
2013-05-23 17:47
225 查看
------------android培训、java培训、期待与您交流!------------
1,JDK1.5新特性:1)静态导入。importstatic
java.lang.Math
2)可变参数。VariableParameter.java。必须放在参数列表末尾。实际内部封装成数组。
3)增强for循环。遍历的集合为数组或实现Iterable接口。
4)基本数据类型自动拆箱与装箱。
5)享元设计模式。
6)枚举。
7)注解。
8)Type接口。
9)泛型。
2,MyEclipse的使用技巧。
1)MyEclipse与Eclipse之间的关系,MyEclipse是Eclipse的一个插件,后来MyEclipse把这两个东西打包起来,直接下
载,Eclipse是用java开发的,启动MyEclipse,查看任务管理器可以看到javaw.exe运行。
2)IDE开发工具都支持使用工程化方式管理一个项目的程序开发过程,一般来说一个相对独立的项目就是一个工程,一个项目中涉及的多个java文件,资源文件等用一个工程进行管理。
3) 一个workspace可以包含多个project,一个workspace保留了eclipse的一套环境选项的配置,例如,所使用的javac和java命令等,细节请查看window->preferences。如果要为eclispe再配置一套环境选项,可以再创建一个workspace。
4)一个Perspective(透视图)代表了若干个view的集合,如何显示各种view。
5)设置单个工程的javac和java,选择工程->右键->properties。设置整个工作间的javac和java,Window->Properties->java进行设置。高版本的可以运行低版本编译的程序。
6)快捷键使用技巧:配置Window->Properties->General->keys,设置alt+/键进行内容提示(Content
Assist)时,要注意解除alt+/键原来的绑定关系,直接输入alt+/就可以找到它的绑定关系,删除绑定关系时也可以使用remove
binding这个按钮。代码模板的设置位置:Window->Properties->java->editor->Templates。
7)调试程序,设置断点->Debug As ->选中变量->右键Watch->走一步。即可查看某一变量的值的变化。
8)在工作间导入新的工程,复制到工程到工作间->File->Import->Existing Projects into Workspace->选择工程->Finish。此时JDK环境有可能不一样,需要重新配置。右键->Build Path->Configure Build Path->Libraries->删掉原来的库->AddExternal JARs,若是jar包已拷入工程里,用Add JARs添加,若增加库用Add
Library->User Library。
3,静态导入。
import语句可以导入一个类或某个包中的所有类。
import static语句导入一个类中的某个静态方法或所有的静态方法。
import static java.lang.Math.*;
import com.itheima.day2.AnnotationTest;
public class StaticImport {
public static void main(String[] args){
System.out.println(abs(6-7));
System.out.println(max(2, 5));
}
}
4,可变参数。
overload和override的区别:重载要求函数名相同,参数的顺序,类型或者个数不同,与返回值、修饰符没有关系。而重写要求函数名,参数均与父类相同,返回值可以不同,但必须是父类的子类型。而且子类访问修饰符大于父类,子类的异常小于父类,静态方法和final方法不能被重写。
可变参数出来之前,只能用overload来重载。
public class VariableParameter {
public static void main(String[] args) {
System.out.println(add(1,3,5,6,78));
System.out.println(add(1,3));
}
//可变参数。必须放在参数列表末尾。内部实际封装被成数组。
public static int add(int x,int...args){
int sum=x;
//增强for循环(JDK1.5新特性),查看langspec(java官方提供的语言规范)。目标集合必须实现Iterable接口
for(int arg:args){
sum+=arg;
}
return sum;
}
}
5, 自动拆箱装箱。
public class AutoBox {
public static void main(String[] args) {
//自动装箱
Integer iObj=3;
//自动拆箱
System.out.println(iObj+12);
//一个字节,值-128~127内。把这个对象在堆内存的常量池中缓存起来,这些对象具有小而多的特点,这些对象具有相同的属性,称为这些对象的内部状态。这时这个对象只创建一次,别的地方引用即可,即享元设计模式。flyweight
再深入理解享元、枚举,实际都是一种单例模式。
Integer i1=13;
Integer i2=13;
System.out.println(i1==i2); //true,一个对象
Integer i3=137;
Integer i4=137;
System.out.println(i3==i4); //false ,两个不同的对象
Integer i5=Integer.valueOf(123);
Integer i6=Integer.valueOf(123);
System.out.println(i3==i4); //true, 一个对象
Integer i7=new Integer(123);
Integer i8=new Integer(123);
System.out.println(i7==i8); //false , 两个不同的对象
}
}
6,枚举。
1)用普通的java类来模拟一个枚举类。
public abstract class WeekDay1 {
private WeekDay1(){} //构造函数私有化,不允许外部创建对象。
public abstract WeekDay1 nextDay(); //定义一个抽象的方法。
public final static WeekDay1 SUN=new WeekDay1(){ //重写nextDay方法。
public WeekDay1 nextDay() {
return MON;
}
};
public final static WeekDay1 MON=new WeekDay1(){
public WeekDay1 nextDay() {
return TUES;
}
};
//重写toString()方法
public String toString(){
if(this==SUN){
return "SUN";
}else
return "MON";
}
}
}
2)//测试枚举。
public class EnumTest {
public static void main(String[] args) {
//测试我们模拟的枚举类。
WeekDay1 weekDay=WeekDay1.MON;
System.out.println(weekDay.nextDay().toString());
/*
* 新来的程序员如何知道weekDay赋7还是0?且编译时不报错
* 这就是个问题?解决:枚举。
* 定义一个WeekDay weekDay=0; 以后用WeekDay这个类型定义的值只能是提前规定好的值,否则编译时就报错,这就是枚举。
*/
//测试我们利用API做的枚举。
WeekDay weekDay2=WeekDay.FRI;
System.out.println(weekDay2); //枚举自动帮我们复写了toString方法。
System.out.println(weekDay2.name());
System.out.println(weekDay2.ordinal()); //枚举对象排第几。
System.out.println(weekDay2.getClass()); //得到该对象的字节码对象。
//枚举提供的静态方法。
System.out.println(WeekDay.valueOf("SUN").toString()); //把字符串“SUN”变成对象。
System.out.println(WeekDay.values().length); //得到枚举类中的对象数组。
System.out.println(TrafficLamp.RED.nextLamp());
}
3)//利用API定义一个带方法和带构造方法的枚举类,此处作为内部类。
public enum WeekDay{
//枚举元素必须位于所有成员之前,若之后有成分,必须在元素后加分号
SUN,MON(3),TUES,WED,THUR,FRI,SAT;
//定义一个不带参数的构造方法。枚举的构造方法必须是私有的。
private WeekDay(){System.out.println("first");}
//定义一个带参数的构造方法。
private WeekDay(int day){System.out.println("second");}
}
4)//定义一个交通灯的枚举类。这个枚举带有抽象方法。
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;
}
}
}
注意 :当枚举只有一个成员时,可以作为一种单例的实现方式。
7,反射。JDK1.2的特性。
Class类,它的对象代表内存中的一份字节码。
反射就是把java类中的各种成分映射成相应的java类。
例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类中的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的类的实例对象来表示,他们是Field,Method,Contructor,Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。得到字节码对应的实例对象的3种方法:
1)类名.class,例如:System.class
2)对象.getClass(),例如:new
Date().getClass()
3)Class.forName(“类名”),例如:Class.forName(“java.util.Date”)
//第三种方式,把类名作为一个参数传进来。写源程序时还不知道类的名字,它的名字是在运行时作为参数传进来的,所以反射通常用这种方式。
九个预定义的Class对象:八个基本类型+void
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.util.Arrays;
public class ReflectTest {
public static void main(String[] args)throws Exception{
String str1="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(cls2==cls3); //true,可知在内存中只有一份字节码。
//Class类的一些基本方法。
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.equals(Integer.class)); //没有复写Object的equals,代表比较Class对象的引用。
System.out.println(int.class==Integer.TYPE); //true,包装类型.TYPE代表他所包装的基本类型的字节码。
System.out.println(int[].class.isPrimitive() ); //false,数组不是原子类型
System.out.println(int[].class.isArray()); //true,判断Class对象是否是数组字节码
//总之,在源程序中出现的类型,都有各自的Class实例对象。 例如:int[ ],void
1)//将Class对象中的构造方法反射到Constructor对象
//用反射来实现new String(new StringBuffer("abc"));
//只接受参数类型是StringBuffer的一个构造方法。
Constructor constructor1=String.class.getConstructor(StringBuffer.class);
String str2=(String)constructor1.newInstance(/*"abc"*/new StringBuffer("abc"));
System.out.println(str2.charAt(2));
//Class提供的newInstance() 直接可以new对象,但只可new一个无参的对象。查看API源代码可知它内部实际就是上述反射操作,但它把这个空参数的构造方法缓存起来,以便下次直接使用,这也说明的反射比较耗费资源。
2)//将Class对象中的成员变量反射到Field对象
ReflectPoint pt1=new ReflectPoint(3,5);
//fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。
Field fieldY=pt1.getClass().getField("y");
Field fieldX=pt1.getClass().getDeclaredField("x"); //得到私有的x变量。
System.out.println(fieldY.get(pt1));
fieldX.setAccessible(true); ·//暴力反射私有变量。
System.out.println(fieldX.get(pt1));
//调用自定义的方法改变String变量的值
changeStringValue(pt1);
//反射点已复写toString方法,可以直接打印对象了。
System.out.println(pt1);
3)//将Class对象中的方法反射到Method对象
//用反射来实现这个操作str1.charAt(1);
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));
//第一个参数为null,则methodCharAt肯定是一个静态方法的对象。
//System.out.println(methodCharAt.invoke(null, 1));
//按JDK1.4的语法调用,没有可变参数,用Object数组。
System.out.println(methodCharAt.invoke(str1, new Object[]{Integer.valueOf(2) /*2自动装箱*/}));
//自己写程序去调用人家的main方法。
//用反射实现TestArguments.main(new String[]{"zhangsan","lisi","wangwu"});
//为什么要用反射?提前不知类的名字,用参数传递的方式把类名传进来,执行时,传进来哪个类执行哪个类。
String startClassName = args[0];
Method main = Class.forName(startClassName).getMethod("main", String[].class);
main.invoke(null, new String[]{"111","222","333"}); //编译报错java.lang.ArrayIndexOutOfBoundsException,自动拆包一次,变成3个参数。
//编译器为兼容1.4版本,对字符串数组自动拆包一次。
解决方法1:
main.invoke(null, new Object[]{new String[]{"111","222","333"}});
解决方法2:
main.invoke(null, (Object)new String[]{"111","222","333"});
4)//数组的反射。
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
System.out.println(a1.getClass() == a2.getClass()); //true,具有相同维度且具有相同的类型。
System.out.println(a1.getClass() == a4.getClass()); //false
System.out.println(a1.getClass() == a3.getClass()); //false
System.out.println(a1.getClass().getSuperclass().getName()); //java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName()); //java.lang.Object
Object obj1 = a1;
Object obj2 = a4;
//Object[] obj3 = a1; int类型不是Object类型。
Object[] obj4 = a3;
Object[] obj5 = a4;
System.out.println(a1);
System.out.println(a4);
//Arrays.asList()方法处理int[]和String[]时的差异。int[]本身就是一个对象,把它看为一个整体。
System.out.println(Arrays.asList(a1)); //[[I@efb549]
System.out.println(Arrays.asList(a4)); //[a,b,c]
//Array工具类,用于完成对数组的反射操作。
printObject(a1);
printObject("xyz");
}
//自定义方法数组反射。
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);
}
}
//自定义一个改变反射点对象的String变量值的方法。
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
Field[] fields=obj.getClass().getFields(); //得到所有的成员变量
for (Field field : fields) { //遍历成员变量数组
//if(field.getType().equals(String.class)){
//字节码只有一份,用==比,语义更明确。
if(field.getType() == String.class) { //如果成员变量的类型是String,则进行以下操作。
String oldValue = (String)field.get(obj); //得到某一对象的成员变量
String newValue = oldValue.replace('b', 'a'); //把该变量的b字符换成a字符。
field.set(obj, newValue); //把新的字符串赋给该对象的String变量。
}
}
}
}
//用发射来执行这个类
class TestArguments {
public static void main(String[] args){
for (String arg : args) {
System.out.println(arg);
}
}
}
//定义一个反射点,专门用来做反射用。
import java.util.Date;
public class ReflectPoint {
private Date birthday
= new Date();
private int x;
public int y;
public String str1="ball";
public String str2="basketball";
public String str3="itheima";
public ReflectPoint(int
x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
//hashCode(),应用在底层必须是hash表的数据结构,先算出一个区域。
public int hashCode()
{
final int
prime = 31;
int result
= 1;
result = prime
* result + x;
result = prime
* result + y;
return result;
}
@Override
//参数写成ReflectPoint pt,equals()为重载,不是覆盖。
public boolean equals(Object
obj) {
if (this ==
obj)
return
true;
if (obj ==
null)
return
false;
if (getClass()
!= obj.getClass())
return
false;
ReflectPoint
other = (ReflectPoint) obj;
if (x != other.x)
return
false;
if (y != other.y)
return
false;
return true;
}
public int getX()
{
return x;
}
public void setX(int
x) {
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int
y) {
this.y = y;
}
@Override
public String toString()
{
return str1
+ ":" + str2 + ":" + str3;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date
birthday) {
this.birthday
= birthday;
}
}
8,hashCode()本质和内存泄露,面试重点。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.TreeSet;
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//模拟一个框架,用反射来获取集合。
InputStream ips = new FileInputStream("config.properties"); // 尽量面向父类或接口编程。
//实际开发中,没人用这种相对路径。
//解决方法1(最常用的):使用绝对路径,但不是硬编码,而是运算得出来的。javaWeb中getRealPath()(得到某个工程所在的实际磁盘位置)+内部路径(相对于这个工程的路径),这两个拼起来就得到配置文件的绝对路径。
//解决方法2:利用类加载器,装载classPath文件夹下的配置文件。缺点:只能读出,不能写入。
方案2的第一种方式:
//InputStream ips =
ReflectTest2.class.getClassLoader().getResourceAsStream("com/itheima/day1/config.properties");
这句不能以“/”打头。证明是一个相对路径,相对于classPath指定目录下。
SSH三大框架内部用的就是类加载器加载的原理,所以他的配置文件放在classPath指定的路径下。
方案2的第二种方式:
//利用CLass提供的方法直接获取配置文件,书写更简便。
//InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties");
直接用类提供的方法更聪明,它相对与所在的包下面。
当然用类提供的方法也可以使用绝对路径,以“/"打头,表示相对于classpath的根目录,与类加载器道理一样了。
InputStream ips = ReflectTest2.class.getResourceAsStream("/com/itheima/day1/resources/config.properties");
Properties props = new Properties();
//Properties是一个增强的Map集合,可以直接从硬盘上获取key,value值到内存。
props.load(ips);
ips.close(); //用完马上关闭,否则会有内存泄露,不是对象不被释放而是对象关联的系统资源不被释放。对象ips的释放由java的垃圾回收器释放。
String className = props.getProperty("className");
Collection collections = (Collection)Class.forName(className).newInstance();
Collection collections = new HashSet(); //这个集合来保证元素唯一,复写hashCode()和equals()方法。
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
//更改变量y的值,利用hashCode()找不到pt1对象,pt1无法删除,造成内存泄露。内存泄露,即某一对象不再用,它一直占用内存空间,而不被释放掉。
pt1.y = 7;
System.out.println(collections.remove(pt1));
System.out.println(collections.size());
}
}
9,反射的主要应用:实现框架功能。例如 struts,spring,hibernate 。
例如:1)在我们还没有写程序时,框架就已经写好了。
2)还有就是上述7中利用反射Method方法调用别人类的main方法,当别人的类还没有定义出来时,我们的类就可以编译了。
框架和工具类的区别,工具类是被调用,框架是调用别人。
模拟一个框架。
1)定义一个配置文件config.properties。 className=java.util.ArrayList。运行时用户只用改一下配置文件就Ola。
2) 查看上述8的程序,用反射来模拟一个框架,实现集合创建。
10,反射的第二种应用:内省IntroSpector。用来对JavaBean操作。
JavaBean是一个特殊的java类,这种java类内部的方法名称符合某种约定的规则。例如setAge(),getAge()。
一个符合JavaBean特点的类可以当普通类来用,但普通Java类不一定能当作JavaBean来处理。
JavaBean主要用来传递数据信息,所以如果两个模块之间传递多个信息,可以将这些信息封装在JavaBean中。这种Java类的方法主要用于访问私有的字段。
JavaBean的特点:
1)JavaBean必须是个公开的类,即它的访问权限必须是public的。javaBean是为了给其他类来用,所以其方法一般都是Public类型的。
2)JavaBean必须具有一个无参数的构造方法。如果在JavaBean中定义了自定义的有参构造方法,就必须添加一个无参数的构造方法,否则将无法设置属性;如果没有定义自定义的有参构造方法,则可以利用编译器自动添加的无参构造方法。
3)JavaBean一般将属性设置成私有的,通过使用getXXX()和setXXX()方法来进行属性的取得和设置。
javaBean的属性是根据set,get方法得到的:去掉get和set后就是JavaBean类的属性,属性命名规则,如果第二个字母是小的,则把第一个字母变成小的Age-->age。如果第二个字母是大的,则第一个字母保持不变CPU-->CPU。
接下来用内省的方式来完成对javaBean的操作。查阅下篇日记高新技术2。
11,常用英语:
java ee——Java Platform,Enterprise
Edition
IDE——IntegratedDevelopment Environment,集成开发环境
jms ——Java消息服务(JavaMessageService)
JMX ——JavaManagementExtensions,即Java管理扩展
JNDI——JavaNamingandDirectoryInterface,Java命名和目录接口
------------android培训、java培训、期待与您交流!------------
详情请查看:http://edu.csdn.net/heima
1,JDK1.5新特性:1)静态导入。importstatic
java.lang.Math
2)可变参数。VariableParameter.java。必须放在参数列表末尾。实际内部封装成数组。
3)增强for循环。遍历的集合为数组或实现Iterable接口。
4)基本数据类型自动拆箱与装箱。
5)享元设计模式。
6)枚举。
7)注解。
8)Type接口。
9)泛型。
2,MyEclipse的使用技巧。
1)MyEclipse与Eclipse之间的关系,MyEclipse是Eclipse的一个插件,后来MyEclipse把这两个东西打包起来,直接下
载,Eclipse是用java开发的,启动MyEclipse,查看任务管理器可以看到javaw.exe运行。
2)IDE开发工具都支持使用工程化方式管理一个项目的程序开发过程,一般来说一个相对独立的项目就是一个工程,一个项目中涉及的多个java文件,资源文件等用一个工程进行管理。
3) 一个workspace可以包含多个project,一个workspace保留了eclipse的一套环境选项的配置,例如,所使用的javac和java命令等,细节请查看window->preferences。如果要为eclispe再配置一套环境选项,可以再创建一个workspace。
4)一个Perspective(透视图)代表了若干个view的集合,如何显示各种view。
5)设置单个工程的javac和java,选择工程->右键->properties。设置整个工作间的javac和java,Window->Properties->java进行设置。高版本的可以运行低版本编译的程序。
6)快捷键使用技巧:配置Window->Properties->General->keys,设置alt+/键进行内容提示(Content
Assist)时,要注意解除alt+/键原来的绑定关系,直接输入alt+/就可以找到它的绑定关系,删除绑定关系时也可以使用remove
binding这个按钮。代码模板的设置位置:Window->Properties->java->editor->Templates。
7)调试程序,设置断点->Debug As ->选中变量->右键Watch->走一步。即可查看某一变量的值的变化。
8)在工作间导入新的工程,复制到工程到工作间->File->Import->Existing Projects into Workspace->选择工程->Finish。此时JDK环境有可能不一样,需要重新配置。右键->Build Path->Configure Build Path->Libraries->删掉原来的库->AddExternal JARs,若是jar包已拷入工程里,用Add JARs添加,若增加库用Add
Library->User Library。
3,静态导入。
import语句可以导入一个类或某个包中的所有类。
import static语句导入一个类中的某个静态方法或所有的静态方法。
import static java.lang.Math.*;
import com.itheima.day2.AnnotationTest;
public class StaticImport {
public static void main(String[] args){
System.out.println(abs(6-7));
System.out.println(max(2, 5));
}
}
4,可变参数。
overload和override的区别:重载要求函数名相同,参数的顺序,类型或者个数不同,与返回值、修饰符没有关系。而重写要求函数名,参数均与父类相同,返回值可以不同,但必须是父类的子类型。而且子类访问修饰符大于父类,子类的异常小于父类,静态方法和final方法不能被重写。
可变参数出来之前,只能用overload来重载。
public class VariableParameter {
public static void main(String[] args) {
System.out.println(add(1,3,5,6,78));
System.out.println(add(1,3));
}
//可变参数。必须放在参数列表末尾。内部实际封装被成数组。
public static int add(int x,int...args){
int sum=x;
//增强for循环(JDK1.5新特性),查看langspec(java官方提供的语言规范)。目标集合必须实现Iterable接口
for(int arg:args){
sum+=arg;
}
return sum;
}
}
5, 自动拆箱装箱。
public class AutoBox {
public static void main(String[] args) {
//自动装箱
Integer iObj=3;
//自动拆箱
System.out.println(iObj+12);
//一个字节,值-128~127内。把这个对象在堆内存的常量池中缓存起来,这些对象具有小而多的特点,这些对象具有相同的属性,称为这些对象的内部状态。这时这个对象只创建一次,别的地方引用即可,即享元设计模式。flyweight
再深入理解享元、枚举,实际都是一种单例模式。
Integer i1=13;
Integer i2=13;
System.out.println(i1==i2); //true,一个对象
Integer i3=137;
Integer i4=137;
System.out.println(i3==i4); //false ,两个不同的对象
Integer i5=Integer.valueOf(123);
Integer i6=Integer.valueOf(123);
System.out.println(i3==i4); //true, 一个对象
Integer i7=new Integer(123);
Integer i8=new Integer(123);
System.out.println(i7==i8); //false , 两个不同的对象
}
}
6,枚举。
1)用普通的java类来模拟一个枚举类。
public abstract class WeekDay1 {
private WeekDay1(){} //构造函数私有化,不允许外部创建对象。
public abstract WeekDay1 nextDay(); //定义一个抽象的方法。
public final static WeekDay1 SUN=new WeekDay1(){ //重写nextDay方法。
public WeekDay1 nextDay() {
return MON;
}
};
public final static WeekDay1 MON=new WeekDay1(){
public WeekDay1 nextDay() {
return TUES;
}
};
//重写toString()方法
public String toString(){
if(this==SUN){
return "SUN";
}else
return "MON";
}
}
}
2)//测试枚举。
public class EnumTest {
public static void main(String[] args) {
//测试我们模拟的枚举类。
WeekDay1 weekDay=WeekDay1.MON;
System.out.println(weekDay.nextDay().toString());
/*
* 新来的程序员如何知道weekDay赋7还是0?且编译时不报错
* 这就是个问题?解决:枚举。
* 定义一个WeekDay weekDay=0; 以后用WeekDay这个类型定义的值只能是提前规定好的值,否则编译时就报错,这就是枚举。
*/
//测试我们利用API做的枚举。
WeekDay weekDay2=WeekDay.FRI;
System.out.println(weekDay2); //枚举自动帮我们复写了toString方法。
System.out.println(weekDay2.name());
System.out.println(weekDay2.ordinal()); //枚举对象排第几。
System.out.println(weekDay2.getClass()); //得到该对象的字节码对象。
//枚举提供的静态方法。
System.out.println(WeekDay.valueOf("SUN").toString()); //把字符串“SUN”变成对象。
System.out.println(WeekDay.values().length); //得到枚举类中的对象数组。
System.out.println(TrafficLamp.RED.nextLamp());
}
3)//利用API定义一个带方法和带构造方法的枚举类,此处作为内部类。
public enum WeekDay{
//枚举元素必须位于所有成员之前,若之后有成分,必须在元素后加分号
SUN,MON(3),TUES,WED,THUR,FRI,SAT;
//定义一个不带参数的构造方法。枚举的构造方法必须是私有的。
private WeekDay(){System.out.println("first");}
//定义一个带参数的构造方法。
private WeekDay(int day){System.out.println("second");}
}
4)//定义一个交通灯的枚举类。这个枚举带有抽象方法。
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;
}
}
}
注意 :当枚举只有一个成员时,可以作为一种单例的实现方式。
7,反射。JDK1.2的特性。
Class类,它的对象代表内存中的一份字节码。
反射就是把java类中的各种成分映射成相应的java类。
例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类中的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的类的实例对象来表示,他们是Field,Method,Contructor,Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。得到字节码对应的实例对象的3种方法:
1)类名.class,例如:System.class
2)对象.getClass(),例如:new
Date().getClass()
3)Class.forName(“类名”),例如:Class.forName(“java.util.Date”)
//第三种方式,把类名作为一个参数传进来。写源程序时还不知道类的名字,它的名字是在运行时作为参数传进来的,所以反射通常用这种方式。
九个预定义的Class对象:八个基本类型+void
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.util.Arrays;
public class ReflectTest {
public static void main(String[] args)throws Exception{
String str1="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(cls2==cls3); //true,可知在内存中只有一份字节码。
//Class类的一些基本方法。
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.equals(Integer.class)); //没有复写Object的equals,代表比较Class对象的引用。
System.out.println(int.class==Integer.TYPE); //true,包装类型.TYPE代表他所包装的基本类型的字节码。
System.out.println(int[].class.isPrimitive() ); //false,数组不是原子类型
System.out.println(int[].class.isArray()); //true,判断Class对象是否是数组字节码
//总之,在源程序中出现的类型,都有各自的Class实例对象。 例如:int[ ],void
1)//将Class对象中的构造方法反射到Constructor对象
//用反射来实现new String(new StringBuffer("abc"));
//只接受参数类型是StringBuffer的一个构造方法。
Constructor constructor1=String.class.getConstructor(StringBuffer.class);
String str2=(String)constructor1.newInstance(/*"abc"*/new StringBuffer("abc"));
System.out.println(str2.charAt(2));
//Class提供的newInstance() 直接可以new对象,但只可new一个无参的对象。查看API源代码可知它内部实际就是上述反射操作,但它把这个空参数的构造方法缓存起来,以便下次直接使用,这也说明的反射比较耗费资源。
2)//将Class对象中的成员变量反射到Field对象
ReflectPoint pt1=new ReflectPoint(3,5);
//fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。
Field fieldY=pt1.getClass().getField("y");
Field fieldX=pt1.getClass().getDeclaredField("x"); //得到私有的x变量。
System.out.println(fieldY.get(pt1));
fieldX.setAccessible(true); ·//暴力反射私有变量。
System.out.println(fieldX.get(pt1));
//调用自定义的方法改变String变量的值
changeStringValue(pt1);
//反射点已复写toString方法,可以直接打印对象了。
System.out.println(pt1);
3)//将Class对象中的方法反射到Method对象
//用反射来实现这个操作str1.charAt(1);
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));
//第一个参数为null,则methodCharAt肯定是一个静态方法的对象。
//System.out.println(methodCharAt.invoke(null, 1));
//按JDK1.4的语法调用,没有可变参数,用Object数组。
System.out.println(methodCharAt.invoke(str1, new Object[]{Integer.valueOf(2) /*2自动装箱*/}));
//自己写程序去调用人家的main方法。
//用反射实现TestArguments.main(new String[]{"zhangsan","lisi","wangwu"});
//为什么要用反射?提前不知类的名字,用参数传递的方式把类名传进来,执行时,传进来哪个类执行哪个类。
String startClassName = args[0];
Method main = Class.forName(startClassName).getMethod("main", String[].class);
main.invoke(null, new String[]{"111","222","333"}); //编译报错java.lang.ArrayIndexOutOfBoundsException,自动拆包一次,变成3个参数。
//编译器为兼容1.4版本,对字符串数组自动拆包一次。
解决方法1:
main.invoke(null, new Object[]{new String[]{"111","222","333"}});
解决方法2:
main.invoke(null, (Object)new String[]{"111","222","333"});
4)//数组的反射。
int[] a1=new int[]{1,2,3};
int[] a2=new int[4];
int[][] a3=new int[2][3];
String[] a4=new String[]{"a","b","c"};
System.out.println(a1.getClass() == a2.getClass()); //true,具有相同维度且具有相同的类型。
System.out.println(a1.getClass() == a4.getClass()); //false
System.out.println(a1.getClass() == a3.getClass()); //false
System.out.println(a1.getClass().getSuperclass().getName()); //java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName()); //java.lang.Object
Object obj1 = a1;
Object obj2 = a4;
//Object[] obj3 = a1; int类型不是Object类型。
Object[] obj4 = a3;
Object[] obj5 = a4;
System.out.println(a1);
System.out.println(a4);
//Arrays.asList()方法处理int[]和String[]时的差异。int[]本身就是一个对象,把它看为一个整体。
System.out.println(Arrays.asList(a1)); //[[I@efb549]
System.out.println(Arrays.asList(a4)); //[a,b,c]
//Array工具类,用于完成对数组的反射操作。
printObject(a1);
printObject("xyz");
}
//自定义方法数组反射。
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);
}
}
//自定义一个改变反射点对象的String变量值的方法。
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
Field[] fields=obj.getClass().getFields(); //得到所有的成员变量
for (Field field : fields) { //遍历成员变量数组
//if(field.getType().equals(String.class)){
//字节码只有一份,用==比,语义更明确。
if(field.getType() == String.class) { //如果成员变量的类型是String,则进行以下操作。
String oldValue = (String)field.get(obj); //得到某一对象的成员变量
String newValue = oldValue.replace('b', 'a'); //把该变量的b字符换成a字符。
field.set(obj, newValue); //把新的字符串赋给该对象的String变量。
}
}
}
}
//用发射来执行这个类
class TestArguments {
public static void main(String[] args){
for (String arg : args) {
System.out.println(arg);
}
}
}
//定义一个反射点,专门用来做反射用。
import java.util.Date;
public class ReflectPoint {
private Date birthday
= new Date();
private int x;
public int y;
public String str1="ball";
public String str2="basketball";
public String str3="itheima";
public ReflectPoint(int
x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
//hashCode(),应用在底层必须是hash表的数据结构,先算出一个区域。
public int hashCode()
{
final int
prime = 31;
int result
= 1;
result = prime
* result + x;
result = prime
* result + y;
return result;
}
@Override
//参数写成ReflectPoint pt,equals()为重载,不是覆盖。
public boolean equals(Object
obj) {
if (this ==
obj)
return
true;
if (obj ==
null)
return
false;
if (getClass()
!= obj.getClass())
return
false;
ReflectPoint
other = (ReflectPoint) obj;
if (x != other.x)
return
false;
if (y != other.y)
return
false;
return true;
}
public int getX()
{
return x;
}
public void setX(int
x) {
this.x = x;
}
public int getY()
{
return y;
}
public void setY(int
y) {
this.y = y;
}
@Override
public String toString()
{
return str1
+ ":" + str2 + ":" + str3;
}
public Date getBirthday()
{
return birthday;
}
public void setBirthday(Date
birthday) {
this.birthday
= birthday;
}
}
8,hashCode()本质和内存泄露,面试重点。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.TreeSet;
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//模拟一个框架,用反射来获取集合。
InputStream ips = new FileInputStream("config.properties"); // 尽量面向父类或接口编程。
//实际开发中,没人用这种相对路径。
//解决方法1(最常用的):使用绝对路径,但不是硬编码,而是运算得出来的。javaWeb中getRealPath()(得到某个工程所在的实际磁盘位置)+内部路径(相对于这个工程的路径),这两个拼起来就得到配置文件的绝对路径。
//解决方法2:利用类加载器,装载classPath文件夹下的配置文件。缺点:只能读出,不能写入。
方案2的第一种方式:
//InputStream ips =
ReflectTest2.class.getClassLoader().getResourceAsStream("com/itheima/day1/config.properties");
这句不能以“/”打头。证明是一个相对路径,相对于classPath指定目录下。
SSH三大框架内部用的就是类加载器加载的原理,所以他的配置文件放在classPath指定的路径下。
方案2的第二种方式:
//利用CLass提供的方法直接获取配置文件,书写更简便。
//InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties");
直接用类提供的方法更聪明,它相对与所在的包下面。
当然用类提供的方法也可以使用绝对路径,以“/"打头,表示相对于classpath的根目录,与类加载器道理一样了。
InputStream ips = ReflectTest2.class.getResourceAsStream("/com/itheima/day1/resources/config.properties");
Properties props = new Properties();
//Properties是一个增强的Map集合,可以直接从硬盘上获取key,value值到内存。
props.load(ips);
ips.close(); //用完马上关闭,否则会有内存泄露,不是对象不被释放而是对象关联的系统资源不被释放。对象ips的释放由java的垃圾回收器释放。
String className = props.getProperty("className");
Collection collections = (Collection)Class.forName(className).newInstance();
Collection collections = new HashSet(); //这个集合来保证元素唯一,复写hashCode()和equals()方法。
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
//更改变量y的值,利用hashCode()找不到pt1对象,pt1无法删除,造成内存泄露。内存泄露,即某一对象不再用,它一直占用内存空间,而不被释放掉。
pt1.y = 7;
System.out.println(collections.remove(pt1));
System.out.println(collections.size());
}
}
9,反射的主要应用:实现框架功能。例如 struts,spring,hibernate 。
例如:1)在我们还没有写程序时,框架就已经写好了。
2)还有就是上述7中利用反射Method方法调用别人类的main方法,当别人的类还没有定义出来时,我们的类就可以编译了。
框架和工具类的区别,工具类是被调用,框架是调用别人。
模拟一个框架。
1)定义一个配置文件config.properties。 className=java.util.ArrayList。运行时用户只用改一下配置文件就Ola。
2) 查看上述8的程序,用反射来模拟一个框架,实现集合创建。
10,反射的第二种应用:内省IntroSpector。用来对JavaBean操作。
JavaBean是一个特殊的java类,这种java类内部的方法名称符合某种约定的规则。例如setAge(),getAge()。
一个符合JavaBean特点的类可以当普通类来用,但普通Java类不一定能当作JavaBean来处理。
JavaBean主要用来传递数据信息,所以如果两个模块之间传递多个信息,可以将这些信息封装在JavaBean中。这种Java类的方法主要用于访问私有的字段。
JavaBean的特点:
1)JavaBean必须是个公开的类,即它的访问权限必须是public的。javaBean是为了给其他类来用,所以其方法一般都是Public类型的。
2)JavaBean必须具有一个无参数的构造方法。如果在JavaBean中定义了自定义的有参构造方法,就必须添加一个无参数的构造方法,否则将无法设置属性;如果没有定义自定义的有参构造方法,则可以利用编译器自动添加的无参构造方法。
3)JavaBean一般将属性设置成私有的,通过使用getXXX()和setXXX()方法来进行属性的取得和设置。
javaBean的属性是根据set,get方法得到的:去掉get和set后就是JavaBean类的属性,属性命名规则,如果第二个字母是小的,则把第一个字母变成小的Age-->age。如果第二个字母是大的,则第一个字母保持不变CPU-->CPU。
接下来用内省的方式来完成对javaBean的操作。查阅下篇日记高新技术2。
11,常用英语:
java ee——Java Platform,Enterprise
Edition
IDE——IntegratedDevelopment Environment,集成开发环境
jms ——Java消息服务(JavaMessageService)
JMX ——JavaManagementExtensions,即Java管理扩展
JNDI——JavaNamingandDirectoryInterface,Java命名和目录接口
------------android培训、java培训、期待与您交流!------------
详情请查看:http://edu.csdn.net/heima
相关文章推荐
- 黑马程序员_java基础之反射及高新技术
- 黑马程序员——JAVA学习笔记十四(高新技术三)
- 黑马程序员——Java高新技术(4)
- 黑马程序员--java高新技术--java5的注解
- 黑马程序员-java专题系列之4-高新技术
- 黑马程序员-Java高新技术(三)
- 黑马程序员——java高新技术2学习笔记整理
- 黑马程序员——java高新技术——网络编程
- 黑马程序员--java高新技术
- 黑马程序员_Java 高新技术(1)
- 黑马程序员_2010年Java高新技术
- 黑马程序员_java学习日记_Java高新技术_Eclipse快捷键
- 黑马程序员——【Java高新技术】——JavaBean、注解
- 黑马程序员 Java高新技术 四
- 黑马程序员——java高新技术学习日记(3)
- 黑马程序员 Java自学总结二十 Java高新技术第三天
- 黑马程序员-java高新技术(反射)
- 黑马程序员-java 高新技术 反射机制
- 黑马程序员_Java高新技术_静态导入
- 黑马程序员 java高新技术<一>--eclipse开发工具、java5的一些简单新特性