静态导入:
import语句可以导入一个类或某个包中的所有类
import static语句导入一个类中的某个静态方法或所有静态方法
语法举例:
import static java.lang.Math.sin;
import static java.lang.Math.*;
基本数据类型的自动拆卸与装箱:
自动装箱:
Integer num1 = 12;
自动拆箱:
System.out.println(num1 + 12);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12; 这块相等,<=127都是真的
System.out.println(num1 == num2);
Integer num3 = 129; 这块不相等,因为是对象
Integer num4 = 129;
System.out.println(num3 == num4);
//因为当数值在byte范围内容,对新特性,如果该数值已经存在,则不会再开辟新的空间
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12) ; 这块的道理同上
System.out.println(num5 == num6);
枚举:
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,
否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,
普通变量的方式在开发阶段无法实现这一目标。
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,
例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
public class EnumTest
{
public static void main(String[] args)
{
WeekDay weekDay2 =WeekDay.FRI;//获取枚举常量
//调用枚举类获取枚举值(枚举常量),枚举里的枚举值都是被public final static修饰。
WeekDay weekDay3= WeekDay.SUN;
WeekDay weekDay4= WeekDay.valueOf("WED");//也可以获取枚举常量
System.out.println(weekDay3);//枚举值的名字
System.out.println(weekDay2.name());//枚举值的名字
System.out.println(weekDay2.ordinal()); //枚举值的位置
//返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
System.out.println(WeekDay.valueOf("SUN").toString());
//valueOf返回带指定名称的指定枚举类型的枚举常量。
System.out.println(WeekDay.values().length);
}
public enum WeekDay//枚举就是一个类,在这个例子中定义方法是内部类了
{
//这些枚举值都是public static final的,也就是我们经常所定义的常量方式,
//因此枚举类中的枚举值最好全部大写。
SUN(1),MON(),TUE,WED,THI,FRI,SAT;//必须第一排,这些都是对象
private WeekDay(){System.out.println("first");}//构造方法,必须私有
//构造方法只是在构造枚举值的时候被调用。
private WeekDay(int day){System.out.println("second");}//必须私有
}
}
带方法的枚举
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,
这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法
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;}
}
}
反射:
反射的基石-Class 类
Class 类:类对应的字节码。对应各个类在内存中的字节码(一个类在虚拟机中通常只有一份字节码),
例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
不同的类的字节码是不同的,所以它们在内存中的内容是不同的,
它们的类型是Class类型。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
如何得到各个字节码自己对应的实例对象( Class类型)
类名.class,例如,Class cls1 = Person.class
对象.getClass(),例如,Class cls2 = new Date().getClass()
Class.forName("类名"),例如,Class cls3 = Class.forName("java.util.Date");
获取这个Class对象,有三种方式:
1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
3:使用的Class类中的方法,静态的forName方法。
指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。
上面三种方法得到的是同一份字节码
//两种返回字节码方式,一种是内存中已经存在的字节码,直接获取。
//另一种是内存中不存在那份字节码,需要先用类加载器加载,在获取。
数组类型的Class实例对象
Class.isArray()
八个基本类型加viod:对应Class对象
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)
和关键字 void 也表示为 Class 对象。
方法:
public boolean isPrimitive()判定指定的 Class 对象是否表示一个基本类型。
Integer.TYPE是int类型。
别外:
Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE
定义:反射就是把Java类中的各种成分映射成相应的java类。
具体获取:java类的Class类显然要提供一系列的方法,来获得其中的变量,
方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,
它们是Field、Method、Contructor、Package等等。
Constructor (构造方法类):
Class里的方法:
static Class<?> forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
Constructor<T> getConstructor(Class<?>... parameterTypes) //可变参数
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
Class<?>[] getDeclaredClasses()
返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。
T newInstance()
创建此 Class 对象所表示的类的一个新实例。
得到某个类所有的构造方法:
例子:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
得到构造方法后:
Constructor 的方法
T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,
并用指定的初始化参数初始化该实例。
传入new StringBuffer("abc")对象,创建对应构造方法的对象
String str =(String)constructor.newInstance(new StringBuffer("abc"));
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
(以前获取过constructor的构造方法,可以直接调用Class里的方法创建对象,不用再获取构造方法)
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
java反射对象和new 出来的对象有什么区别?
反射对象是直到程序运行期间才知道类的名字的实例,这时才获取对象的成员,并设置属性。此时要用到类的全路径 ,用类的全路径来加载类,并返回该类的对象。以后就可以遍历类中的各个方法,各个属性。
new是给类直接在内存中创建一个实例,并且可以直接初始化等。不需要类的全路径。
反射会导致程序性能严重下降。
Field 类:(类里的成员变量)
public class ReflectPoint//要运行请将每个public类分别放在不同文本中,然后导包
{
private int x;
public int y;
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
public class ReflectTest1 {
public static void main(String[] args) throws Exception
{
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");
//fieldY的值是多少?是5,错!fieldY不是对象身上的变量,
//而是类上,要用它去取某个对象上对应的值,取值看下面
//暴力反射。获取私有成员变量
Field fieldX =pt1.getClass().getDeclaredField("x");//获取私有成员的字节码,但运行使用不了。
fieldX.setAccessible(true);//设置为true,就可以访问了。
fieldX.get(pt1);//取到了强制使用
//将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
changeStringValue(pt1);
private static void changeStringValue(Object obj) throws Exception
{
Field[] fields = obj.getClass().getFields();
for(Field field : fields)
{
//if(field.getType().equals(String.class)){
//为什么用==,因为字节码为同一份。以后比较字节码都用==
if(field.getType() == String.class){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
}
Method :(类里的方法)
Class里的方法: Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的
指定公共成员方法。
Method里的方法:Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
通常方式: String str ="abc";
str.charAt(1);
反射方法:String str ="abc";
//获取成员方法,反射获取方法,再使用对象来调用方法。如果str1位置为null,及可以知道是静态方法。
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));//1.5getMethod(String name, Class<?>... parameterTypes)
/*methodCharAt.invoke(str1, new Objeck[](2));1.4语法
理解诶1.4语法 new Objeck[](new String("abc"),1);数组里面装数组及其他
new Object[](new String("abc"),1)*/
用反射方式执行某个类中的main方法:
class TestArguments//要运行请将每个public类分别放在不同文本中,然后导包
{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
public class ReflectTest2 {
public static void main(String[] args) throws Exception
{
//写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
//TestArguments.main(new String[]{"111","222","333"});//通常方法
String startingClassName = args[0];//运行时需要传递类名,即java ReflectTest2 包名.需要运行的类名。
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//1.4特性,
//mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
}
}
数组反射:
Class里的方法:
Class<? super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象。
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
注意:基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;
非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
int [] a1 = new int[]{1,2,3};//父类为Object
int [] a2 = new int[4];
int[][] a3 = new int[2][3];
a1与a2的字节码相同,a1与二位数组a2的字节码不相同。
String [] a4 =new String[]{"a","b","c"};//父类为Object
a1与a4的字节码不相同。因为类型不同
结合Object即看上面说明理解下面:
//基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;
Object Obj1 =a1;//int [] a1 = new int[]{1,2,3}
Object[] Obj3 =a3;//int[][] a3 = new int[2][3];
//非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Object Obj2 =a4;//String [] a4 =new String[3];
Object[] Obj4 =a4;//String [] a4 =new String[3];
Arrays 方法:
asList:将数组转换为List集合。1.4与1.5的变化。可变参数。先找1.4,在找1.5
Sop(a1);//[@4AFDSF
Sop(a4);//String:@4AFDSF
Sop(Arrays.asList(a1));//[[@4AFDSF]
Sop(Arrays.asList(a4));//打印出来a,b,c
数组反射例子:
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);
}
}
内存泄露:没被使用,但对象还在内存中占有内存。没关流也会内存泄露,调用的底层资源没关。
public class ReflectPoint
{
public static void main(String[] args) throws Exception
{
Collection col =new HashSet()
ReflectPoint pt1 =new ReflectPoint(3,3);//内存地址不同
ReflectPoint pt2 =new ReflectPoint(5,5);
col.add(pt1);
col.add(pt2);
col.add(pt1);
//添加进去2个元素
pt1.y =7;//修改后,hashCode值改变了,跑到另一个区域里了
col.remove(pt1);.//删除的时候,还是去找原来的位置。所以没删除掉
System.out.println(col.size());//删除了但打印还是2,所以没删除掉
}
}
反射的作用:实现框架功能
框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?
我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,
所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。
框架与工具类:
框架是框架调用类。
工具是类调用工具。
内省:
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,
且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,
这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。
这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,
则需要通过一些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?
JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。(ter为去属性)
如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?
如果方法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?
//注意:去掉set前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。
Age-如果第二个字母是小的,则把第一个字母变成小的-age
setId()的属性名 id
isLast()的属性名 last
setCPU的属性名是什么? CPU
getUPS的属性名是什么? UPS
实例:
public class ReflectPoint //JavaBean,特殊类
{
private int x;
private int y;
public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
}
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;
}
}
//导包
public class IntroSpectorTest //要运行请将每个public类分别放在不同文本中,然后导包
{
public static void main(String[] args) throws Exception
{
ReflectPoint pt = new ReflectPoint(3,5);
//反射的方式:"x"-->"X"-->"getX"-->MethodGetX-->
//下面是内省的方式:"x"-->MethodGetX
String propertyName= "x";//设置属性名
//属性描述符:PropertyDescriptor 描述 Java Bean 通过一对存储器方法导出的一个属性。
//PropertyDescriptor(String propertyName, Class<?> beanClass)
// 通过调用 getFoo 和 setFoo 存取方法,为符合标准 Java 约定的属性构造一个 PropertyDescriptor。
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt.getClass());
//Method getReadMethod() 获得应该用于读取属性值的方法。
Methid methidGetX = pd.getReadMethid();
//Object invoke(Object obj, Object... args)
//对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
Object retVal = methidGetX.invoke(pt);
System.out.println(retVal);
//获得应该用于写入属性值的方法。
Methid methidGetX = pd.getWriteMethid();
//Object invoke(Object obj, Object... args)
// 对带有指定参数的指定对象调用由此 Method 对象表示的底层方
methidGetX.invoke(pt,7);
//打印修改后的x值
System.out.println(pt.getX());
//下面是抽取成方法:
private static void setProperties(Object pt, String propertyName,Object value)
throws IntrospectionException,IllegalAccessException, InvocationTargetException
{
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt.getClass());
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt,value);
}
private static Object getProperty(Object pt, String propertyName)
throws IntrospectionException, IllegalAccessException,InvocationTargetException
{
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt);
/* 从所有属性中获取自己需要的属性(张老师最早使用的代码,有些麻烦,没上面好)
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertyName))
{
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}*/
return retVal;
}
}
Beanutils工具包:和上面的抽取的方法差不多,就是别人已经定义好了的内省方法。
只需要导入别人的jar包使用就可以了