黑马程序员—正则表达式+反射

来源:互联网 发布:苹果6手机壳淘宝 编辑:程序博客网 时间:2024/05/01 16:19

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

正则表达式:

(1):特殊字符

\\:反斜线

\r:回车

\n:换行

 

(2):字符类

[abc]a,b或者c的任意一个。

[^abc]:除了a,b,c以外的字符。

[a-zA-Z]:包括了26个英文字母。

[0-9]:包括了0-9这个10个数字字符。

 

(3):预定义字符类

. 任意字符

\d 数字[0-9]

\D 非数字[^0-9] 

\w 单词字符:[a-zA-Z_0-9] 

\W 非单词字符:[^\w] 

 

(4):边界匹配器

^ 行的开头 

$ 行的结尾 

\b 单词边界 

 

(5)Greedy 数量词

x? 0次或1

x* 0次或多次

x+ 1次或多次

x{n} 恰好n

x{n,} 至少n

x{m,n} 至少m次,但不能超过n

 

 

代码事例:

package day26;

/*

 * 正则表达式:符合某种规则的字符串。

 * 

 * 规则?

 * 

 * 举例:校验qq号码.

 *1:要求必须是5-15位数字

 *2:0不能开头

 */

import java.util.Scanner; public class RegexTest {public static void main(String[] args) {//封装键盘录入@SuppressWarnings("resource")Scanner sc = new Scanner(System.in);System.out.println("请输入QQ号");String qq = sc.nextLine();//用正则表达式做boolean flag = checkQq(qq);System.out.println(flag);} private static boolean checkQq(String qq) { return qq.matches("[1-9]\\d{5,15}");}}


 

 

 

1.概述:是符合某种规则的字符串;是操作字符串的。

 

2.正则表达式的规则字符:

A,字符:

x字符任意字符代表自己本身。

\\ 反斜线字符

\r 回车

\n 换行

B,字符类:

[abc] a,bc,任意字符一次。

[^abc]任意字符除了a,bc

[a-zA-Z] a-zA-Z,两头的字母包括在内。

[0-9] 任意的数字字符一次。

C,预定义字符类

任意的字符。

\d 数字:[0-9]

\w 单词字符:[a-zA-Z  0-9]

单词字符:英文,数字及_

 

D,边界匹配器

^行的开头。

$行的结尾。

\b单词边界(也就是说这里出现的不能是单词字符)

 

E,数量词

F,组

 

 

 

 

3.功能:

A判断功能:

 public boolean matches(String regex)

代码事例:

package day26;

 

import java.util.Scanner;

 

/*

 * 正则表达式的判断功能:

 * public boolean matches(String regex)

 * 看到这个方法,我们应该知道有一个字符串对象调用该方法,还应该有一个字符串规则。

 */

public class RegexDemo1 {public static void main(String[] args) {//校验电话号码/* * 移动电话: * 13436975980 * 13245678901 * 13212345678 * 15812345678 * 15613245678 * 座机: * 010-88786541 * 0311-8867567 */String regex = "1[35]\\d{9}";@SuppressWarnings("resource")Scanner sc =new Scanner(System.in);System.out.println("请输入你的手机号:");String phone = sc.nextLine();boolean flag = phone.matches(regex);System.out.println(flag);}}


 

B切割功能:

public String[] split(String regex)

代码事例:

 

 

/*

 * 切割功能:

 * public String[] split(String regex)

 */

public class RegexDemo2 {public static void main(String[] args) {// 切割字符串"aa,bb,cc";String str = "aa,bb,cc";String regex = ",";String[] strArray = str.split(regex);for (String s : strArray) {System.out.println(s);}System.out.println("**************");// 切割字符串"aa.bb.cc"String str2 = "aa.bb.cc";String regex2 = "\\.";String[] strArray2 = str2.split(regex2);for (String s : strArray2) {System.out.println(s);}System.out.println("**************");// 切割字符串"-1 99 4 23"String str3 = "-1 99 4 23";String regex3 = " ";String[] strArray3 = str3.split(regex3);for (String s : strArray3) {System.out.println(s);}System.out.println("**************");// 切割字符串"-1     99              4 23"String str4 = "-1     99              4 23";String regex4 = " +";String[] strArray4 = str4.split(regex4);for (String s : strArray4) {System.out.println(s);}System.out.println("**************");// 切割字符串"D:\itcast\20131130\day27\code"String str5 = "D:\\itcast\\20131130\\day27\\code";String regex5 = "\\";String[] strArray5 = str5.split(regex5);System.out.println("--------------------");for (String s : strArray5) {//System.out.println("--------------------");System.out.println(s);}System.out.println("--------------------");}}


 

 

C替换功能:

 public String replaceAll(String regex,String replacement)

 用给定的字符串去替换字符串对象中满足正则表达式的字符。

代码事例:

 

/*

 * 替换功能:

 * public String replaceAll(String regex,String replacement)

 * 用给定的字符串去替换字符串对象中满足正则表达式的字符。

 */

public class RegexDemo3 {public static void main(String[] args) {// 用”#”替换叠词:"sdaaafghccccjkqqqqql";String str = "sdaaafghccccjkqqqqql";String regex = "(.)\\1+";String result = str.replaceAll(regex, "#");System.out.println(result);System.out.println("--------------------");// 叠词保留一次String str2 = "sdaaafghccccjkqqqqql";// 叠词是在同一个字符串中用\编号来引用String regex2 = "(.)\\1+";// 在替换方法中,第二个字符串数据中可以使用$编号的形式来引用第一个正则表达式中的组的内容String result2 = str2.replaceAll(regex2, "$1");System.out.println(result2);System.out.println("--------------------"); // 有些论坛不允许发电话,qq号,银行账号等.// 把数字用“*”替换wert13245asfklwyoeworewsfd6744232433fafsString str3 = "wert13245asfklwyoeworewsfd6744232433fafs";// String regex3 = "\\d+";String regex3 = "\\d";String result3 = str3.replaceAll(regex3, "#");System.out.println(result3);}}


 

D获取功能:

使用模式对象Pattern和匹配器对象Mathcher

代码事例:

 

import java.util.regex.Matcher;import java.util.regex.Pattern; /* * 获取功能: * 使用模式对象Pattern和匹配器对象Mathcher */public class RegexDemo4{public static void main(String[] args) {// // 把正则表达式编译成模式对象// Pattern p = Pattern.compile("a*b");// // 通过模式对象调用匹配方法获取到匹配器对象// Matcher m = p.matcher("aaaaab");// // 调动匹配器对象的判断功能// boolean b = m.matches();// System.out.println(b); // 这道题目我们还可以通过判断功能// String regex = "a*b";// String str = "aaaaab";// boolean flag = str.matches(regex);// System.out.println(flag); // 定义规则String regex = "\\b[a-z]{3}\\b"; String str = "da jia zhu yi le, ming tian bu fang jia, xie xie!";// 想要获取3个字符组成的单词 Pattern p = Pattern.compile(regex);Matcher m = p.matcher(str);// public boolean find()// 判断看是否存在有满足条件的子串// boolean flag = m.find();// System.out.println(flag);// //获取子串// //public String group()// String s = m.group();// System.out.println(s);while(m.find()){System.out.println(m.group());}}}


 

 

 

 

 

 

 

反射

透彻分析反射的基础_Class

1) 概述

class

Class--->代表一类什么样的事物?

 

Person类的实例对象

Person p1 = new Person();

Person p2 = new Person();

 

Data

 

Math

Java(Class)的实例对象,对应各个类在内存中的字节码。

 

概念:

所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。

 

 

如何得到各个字节码对应的实例对象(Class类型)--加载字节码的方式:

1.类名.class 

Class cls1 = Data.class//字节码1:表示Data类在内存里的字节码,一旦用到Data类名字,内存里就会出现这个类的字节码,从硬盘里加载进来。

Class cls2 = Person.class//字节码2

 

2.对象.getClass()

p1.getClass();

 

3.Class.forName("java.name.String"),主要用于反射

作用:返回字节码文件

返回的方式有两种:

1) 类的字节码已经加载到内存,直接找到字节码,返回;

2) 虚拟机还没有字节码,用类加载器Class.forName()指定类加载器去加载,缓存起来,然后返回字节码;

该方法有两种形式:

Class.forName(String name, boolean initialize, ClassLoader loader)

参数 name表示的是类的全名;initialize表示是否初始化类;loader表示加载时使用的类加载器。

Class.forName(String className)

相当于设置了参数 initialize的值为 trueloader的值为当前类的类加载器。

Class.forName("Foo")等于Class.forName("Foo", true, this.getClass().getClassLoader())

 

 

九个预定义Class实例对象

表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,

即 booleanbytecharshortintlongfloat 和 double。对应获取Class对象的方法为:

Boolean.TYPE, Byte.TYPE,Character.TYPE,  Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE

 

int.class == Integer.TYPE

boolean isPrimitive() :判定指定的 Class 对象是否表示一个基本类型。 

 

数组类型的Class实例对象

boolean isArray():判定此 Class 对象是否表示一个数组类。

Class.isArray();

 

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

 

3) 使用

class  ReflectTest {public static void main(String[] args) throws Exception{String str = "fff";Class c1 = String.class;Class c2 = str.getClass();Class c3 = Class.forName("java.lang.String");System.out.println(c1==c2);//trueSystem.out.println(c1==c3);//true System.out.println(c1.isPrimitive());//falseSystem.out.println(int.class.isPrimitive());//true//static Class<Integer> TYPE 表示基本类型 int 的 Class 实例。 System.out.println(int.class == Integer.TYPE);//true//boolean isArray():判定此 Class 对象是否表示一个数组类。System.out.println(int[].class.isArray());//true}}


 

 

 

理解反射的概念

反射的概念

 

通过Class实例对象,把java类中的各种成分映射成相应的java类。例如,一个java类是用一个Class类的对象来表示,一个类的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包、数组等信息。这些信息就是用相应的类的实例对象来表示,他们是FieldMethodContructorPackage等等。

 

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象,

 Package getPackage() :获取此类的包。返回的是Package类的实例对象; 

 Method getMethod(String name, Class<?>... parameterTypes) : 返回一个 Method 对象,代表某个类中的一个方法,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

Constructor<T> getConstructor(Class<?>... parameterTypes) :返回一个 Constructor 对象,代表某个类中的一个构造方法,它反映此 Class 对象所表示的类的指定公共构造方法。 

.............

 

问题:

得到这些实例对象后有什么用?怎么用?

构造方法的反射应用

1) 概述

 

构造方法的反射应用

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

 

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

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

 

得到某个类的一个构造方法:

//获得String类的某一个构造方法,只获得参数为一个,且为StringBuffer类型的构造方法

Constructor ct1 = Class.forName(java.lang.String).getConstructor(StringBuffer.class);//获得方法时要用StringBuffer.class类型

 

创建指定构造函数的实例对象(三步:classàConstructorànew Object)

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

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

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

 

创建默认构造函数的实例对象(两步:classà new Object) Class.newInstance()方法

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

 

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

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

 

反射会导致程序性能严重下降

 

 

2) 使用

class  ReflectTest {public static void main(String[] args) throws Exception{//用反射实现类似功能:new String(new StringBuffer("asd"));Constructor con1 = String.class.getConstructor(StringBuffer.class);//编译器只看变量的定义(=左边),不看代码的执行(=右边),只是将右边编程二进制//用构造方法new一个实例,此时创建的实例为Object类型,需要强转String str1 = (String)con1.newInstance(new StringBuffer("asdf"));System.out.println("str1="+str1);String str2 = (String)Class.forName("java.lang.String").newInstance();str2="123";System.out.println("str2="+str2);}}


 

成员变量的反射

1) 概述

Field类:代表某个类中的一个成员变量

问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?

      类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?

 所以字段FieldX代表的是x的定义,而不是具体的x变量

 

2) 使用(获取指定成员变量)

参考类:

public class ReflectPoint {private int x;public int y;public ReflectPoint(int x, int y) {super();this.x = x;this.y = y;}public static void main(String[] args) {System.out.println("我是一个参考类");}}


 

应用类

class  ReflectTest {public static void main(String[] args) throws Exception{ReflectPoint r1 = new ReflectPoint(8,9);Field fieldy = r1.getClass().getField("y");//获得其他类的成员变量,先从其他类中得到字节码,字节码中有成员变量/*fieldy的值?fieldy只代表这个类字节码上的变量,没有对应到对象身上;每一个对象身上都有fieldy,要用它去取某个对象上的值*///取出变量在某个对象上的值System.out.println("fieldy在r1对象上的值 = "+fieldy.get(r1));//Field fieldx = r1.getClass().getField("x");//x是私有的,fieldx变量看不见,不能获得此变量。Field fieldx = r1.getClass().getDeclaredField("x");//x是私有的,只要是声明过的,fieldx都看得见,但是不能访问私有的。fieldx.setAccessible(true);//设置允许访问,暴力反射System.out.println("fieldx在r1对象上的值 = "+fieldx.get(r1));}}


 

3) 什么是暴力反射?

通过反射获取一个具体的变量(构成函数,方法)时,该变量在所属的类中是私有成员变量,不能直接访问,需要先获取该变量getDeclaredField(),然后通过 (setAccessible(true))方法得到他的访问权利。

 

成员变量反射的综合案例(获取部分成员变量)

Class<?> getType() :返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

Object get(Object obj) :返回指定对象上此 Field 表示的字段的值。

void set(Object obj, Object value) :将指定对象变量上此 Field 对象表示的字段设置为指定的新值

请看如下示例:

//参考类

class ReflectPoint1 {public String str1;public String str2;public String str3; public ReflectPoint1(String str1, String str2, String str3) {super();this.str1 = str1;this.str2 = str2;this.str3 = str3;} //要打印对象的属性值,需覆盖toString()方法public String toString(){return str1+";"+str2+";"+str3;}}


//应用类

class  ReflectTest {public static void main(String[] args) throws Exception{ReflectPoint1 rp = new ReflectPoint1("bababa","abdgfbbaaabbbb","string");strchange(rp);System.out.println(rp);}public static void strchange(Object obj)throws Exception{Field[] fi = obj.getClass().getFields();for(Field field:fi){if(field.getType() == String.class);//字节码的比较用==,比较的都是一份字节码;{String strolds = (String)field.get(obj);String strnews = strolds.replace('b','a');field.set(obj,strnews);}}}}


成员方法的反射

1) 概述

成员方法的反射

Method类代表某个类中的一个成员方法;

得到类中的某一个方法:

Method getMethod(String name, Class<?>... parameterTypes) :返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。

name - 方法名

parameterTypes - 参数列表

Method method = String.class.getMethod("charAt", int.class);

 

调用方法:

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

反射方式:method.invoke(str,1);

                如果invoke(null,1),表示该Method对象对应的是一个静态的方法

 

JDK1.4JDK1.5invoke方法的区别

1.4public Object invoke(Object obj,Object[] args)

        将数组作为参数传递给invoke方法,

数组中的每个元素分别对应被调用方法中的一个参数,

所以可写成method.invoke("str",new object[]{1})形式

 

1.5public Object invoke(Object obj,Object... args)传入的参数为可变参数

 

2) 使用

 

class  ReflectTest {public static void main(String[] args) throws Exception{//Object invoke(Object obj, Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 String str = "abcd";//用反射实现str.charAt(1)功能Method m = String.class.getMethod("charAt", int.class);System.out.println(m.invoke(str,1));//jdk1.5  传入可变参数System.out.println(m.invoke(str,new Object[]{2}));//jdk1.4传入数组参数//m.invoke(null,1),表示不用对象就能调用的方法,静态方法}}


 

对接收数组参数的成员方法进行反射

 

1) 概述

对接收数组参数的成员方法进行反射

 

写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。

调用方法

通常方式:类名.main(new String[]{"ttt","www","qq"});

 

反射方式:

String classstartname = args[0];

Method mainmethod =  Class.forName(classstartname).getMethod("main", String[].class);

mainmethod.invoke(null, new Object[]{new String[]{"11","22"}});

 

为什么要用反射去调用?

程序中已知要调用main方法,但是不知道哪个类的main方法,要通过传进来的参数,确定类名,

然后调用传进来的类的main方法。

 

为什么会出现参数个数异常呢?

通过反射的方法调用main方法时,为invoke方法传递参数的方式是:

JDK1.5:整个数组是一个参数,JDK1.4:数组中每一个元素对应一个参数,

当把字符串数组作为参数传递给invoke方法时,JDK1.5肯定要兼容JDK1.4语法,

会按JDK1.4的去处理,即把数组打散成若干单独参数。所以在给main方法传递参数时,

不能使用mainmethod.invoke(null, new String[]{"11","22"});,会出现参数类型不对问题;

 

解决办法:

1.mainmethod.invoke(null, new Object[]{new String[]{"11","22"}});

 

2.mainmethod.invoke(null, (Object)new String[]{"11","22"});

 

2) 使用

 

class  ReflectTest {public static void main(String[] args) throws Exception{//实现在此程序中调用testArguments类的main方法//通常方式testArguments.main(new String[]{"11","22"});//反射方式//假设传进main方法的参数中args[0]就是一个类名,且有main方法String classstartname = args[0];Method mainmethod =  Class.forName(classstartname).getMethod("main", String[].class);/*mainmethod.invoke(null, new String[]{"11","22"});参数个数异常,本来是接收一个数组参数,JDK1.5为了兼容JDK1.4,会将数组拆一次,拆成一个个(多个)参数*///解决办法:1.将数组参数再次封装成数组 ; 2.将数组参数强转成Object对象,就不拆包了;mainmethod.invoke(null, new Object[]{new String[]{"11","22"}});mainmethod.invoke(null, (Object)new String[]{"11","22"});}}class testArguments//按f2出现完整的类名,将完整的类名复制到run configurations界面   //中的Arguments界面的program arguments中,用来配置运行类的main方法参数{public static void main(String args[]){for(String str:args){System.out.println(str);}}}


 

数组与Object的关系及其反射类型

1) 概述

数组的反射

 

数组元素类型相同,维度相同,得到的字节码就是同一份,属于同一个类型,即具有相同的Class实例对象;

 

基本数据类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用,

非基本数据类型的一维数组既可以当做Object类型使用,也可当做Object[]类型使用

 

ClassgetName()方法:

以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称

如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '[' 字符加元素类型名。

元素类型名的编码如下: 

Element Type       Encoding  

boolean  Z  

byte  B  

char  C  

class or interface       Lclassname;  

double  D  

float  F  

int  I  

long  J  

short  S  

类或接口名 classname 是上面指定类的二进制名称。

String.class.getName():returns "java.lang.String"

(new int[3][4][5][6][7][8][9]).getClass().getName():returns "[[[[[[[I"

 

ClassgetSuperclass()方法:

Class<? super T> getSuperclass() :返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超()类的 Class

 

2) 使用

 

class  ReflectTest {public static void main(String[] args) throws Exception{int a0 = 3;int[] a1 = new int[]{1,2};int[] a2 = new int[]{3,4,5};int[][] a3 = new int[][]{{4,4},{3,3},{2,2,2}};String[] a4 = new String[]{"a","b","c"};sop(a1.getClass() == a2.getClass());sop(a1.getClass().getName());//[Isop(a1.getClass().getSuperclass().getName());//java.lang.Objectsop(a4.getClass().getSuperclass().getName());//java.lang.Objectsop(a2.getClass().getSuperclass().getName());//java.lang.Objectsop(a3.getClass().getSuperclass().getName());//java.lang.Objectsop(String.class.getSuperclass().getName());Object o0 = a0;Object o1 = a1;Object o2 = a2;Object o3 = a3;Object o4 = a4;sop(o0);sop(o1);sop(o2);sop(o3);sop(o4);//Object[] b0 = a0;编译不通过,因为a0是个基本数据类型,是个对象//Object[] b1 = a1;编译不通过,因为a1,a2是基本数据类型的一维数组,是个对象,不是对象数组//Object[] b2 = a2;Object[] b3 = a3;//编译通过,因为a3是基本数据类型的二维数组(数组的数组),表示有一个数组,数组里面装的是int[](Objet),是个对象数组Object[] b4 = a4;//编译通过,因为String是Object,String[]等于对象数组//可得出:int(基本数据类型)不是对象,String是对象,所以前者不能转成Object数组,后者可以/*结果没有意义 *sop(a1);sop(a4);*///用Arrays工具类对数组进行操作//JDK1.5:public static <T> List<T> asList(T... a)//JDK1.4:public static List asList(Object[] a)sop(Arrays.asList(a1));//JDK1.5处理sop(Arrays.asList(a3));//JDK1.4处理+JDK1.5处理sop(Arrays.asList(a4));//JDK1.4处理}public  static void sop(Object obj){System.out.println(obj);}}


 

数组的反射应用

1) 概述

数组的反射应用

 

获取,设置,数组的值,获取数组的长度,怎么做?

通过java.lang.reflect.Array类中的方法:

static Object get(Object array, int index) 

          返回指定数组对象中索引组件的值。 

static int getLength(Object array) 

          以 int 形式返回指定数组对象的长度。 

static void set(Object array, int index, Object value) 

          将指定数组对象中索引组件的值设置为指定的新值。 

 

怎么得到数组中的元素类型?没有办法

int [] a = new int[3]

怎么知道a的数组类型?没有办法

因为

Object[] a =new Object[]{"a",1}

a[0].getClass.getName();

a[0].getClass.getName();

只能得到每一个具体元素的类型,不能得到整个数组的元素类型

 

2) 使用

class  ReflectTest {public static void main(String[] args) throws Exception{int a0 = 3;int[] a1 = new int[]{1,2};int[] a2 = new int[]{3,4,5};int[][] a3 = new int[][]{{4,4},{3,3},{2,2,2}};String[] a4 = new String[]{"a","b","c"};sop(a2);sop(a3);sop(a4);sop("asd");}public  static void sop(Object obj)//打印数组或基本数据类型{Class c = obj.getClass();if(c.isArray()){int len = Array.getLength(obj);//通过Array类访问数组,获取数组长度for(int i=0;i<len;i++){System.out.println(Array.get(obj, i));//返回指定数组对象中索引组件的值。}}elseSystem.out.println(obj);}}


 

 

ArrayList_HashSet的比较及Hashcode分析

1) 概述

反射的综合应用--ArrayList_HashSet的比较及Hashcode分析

 

哈希算法

哈希算法提高从集合中查找元素的效率,这种方式将集合分成若干区域,每个要存入哈希集合的对象可以计算出一个哈希码,

可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。

 

面试题1--HashCode()方法的作用?

返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。 

当从hashset集合中查找某个对象时,java系统首先调用对象的hashcode()方法获得该对象的哈希码,然后根据哈希码,

找到相应的存储区域,最后取出该存储区域内的每个元素与该对象进行equals方法比较,不用遍历集合中的所有元素,就可以得到结论。

 

面试题2--内存溢出,内存泄露的例子?

当一个对象被存储进HsahSet集合中后,就不能修改这个对象中的那些参与计算哈希值得字段了,否则,对象修改后的哈希值就不同了,

在这种情况下,去哈希集合中检索对象时,就找不到原来的对象了。这也会导致无法从hashset集合中单独删除当前对象,

从而导致内存泄露。

递归,没有结束标记;

 

提示:

一个类的两个实例对象用equals()方法比较的结果相等时,他们的哈希码也必须相等,但是,

 equals(java.lang.Object) 方法比较结果不相等的对象可以有相同的哈希码,

 或者哈希码相同的两个对象,调用equals()方法比较结果可以不等。

 

2) 使用

1

public class ReflectPoint {private int x;public int y;public String str1="aa";public String str2="ss";public String str3="dd"; public ReflectPoint(int x, int y) {super();this.x = x;this.y = y;} @Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;} @Overridepublic 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;} //要打印对象的属性值,需覆盖toString()方法public String toString(){return str1+";"+str2+";"+str3;}}


 

 

2

 

class  ReflectTest {public static void main(String[] args) throws Exception{Collection c = new HashSet();//new ArrayList();//ReflectPoint r1 = new ReflectPoint(2,2);ReflectPoint r2 = new ReflectPoint(3,3);ReflectPoint r3 = new ReflectPoint(2,2);c.add(r1);c.add(r2);c.add(r3);c.add(r1);r1.y = 8;c.remove(r1);//将r1的y值更改后,哈希值改变,存储区域改变,remove()方法在原本区域里找不到r1的y值了//这样原本该删除的对象没有删掉,又新增对象的话,日积夜累,就会导致内存溢出。sop(c.size());/*list集合是有序可重复的,存的是对象的引用;set集合是无序不可重复的,默认equals()比较的是哈希值,hashcode值是根据内存地址值算出来的,要让r1与r3完全相等,必须同时复写equals(),hashcode()方法(用eclipse自带生成)*/}public  static void sop(Object obj)//打印数组或基本数据类型{Class c = obj.getClass();if(c.isArray()){int len = Array.getLength(obj);//通过Array类访问数组,获取数组长度for(int i=0;i<len;i++){System.out.println(Array.get(obj, i));//返回指定数组对象中索引组件的值。}}elseSystem.out.println(obj);}}


 

 

 

 

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

1) 概述

框架与工具类

从框架内涵的角度一个框架是一个可复用设计,它是由一组抽象类及其实例间协作关系来表达的

从框架用途的角度一个框架是在一个给定的问题领域内,一个应用程序的一部分设计与实现

比如:自己做房子给用户住,用户自己安装门,我做的房子就是框架,用户需要使用我的框架,把门插进我提供的框架中。

框架与工具类有区别,工具类被用户的类调用,而框架是调用用户提供的类。

 

框架要解决的核心问题

我在写框架时,我的框架程序怎样能调用到你以后写的程序呢?

因为在写框架程序时,无法知道要被调用的类名,所以,

在程序中无法直接new某个类的实例对象了,而要用反射的方式(Class.forName(classname).new Instance)来做。

 

使用步骤:

1、先在程序中直接用new语句创建ArrayListHashSet的实例对象,

2、然后创建一个properties文件,配置内容: className=java.util.HashSet //className=java.util.ArrayList

3、在程序中加载进配置文件,用配置文件加反射的方式创建ArrayListHashSet的实例对象

 

提示

IO流对象.close();的底层含义是:先把widowswidow(系统资源,物理资源)关掉,然后由java VM回收本IO流对象

 

2) 使用

配置文件的获取方式

 

1.通过Properties得到资源文件

可以读写配置文件,因为有InputStreamOutputStream

/*资源文件放在project文件夹中,

要用完整的路径,但完整的路径不是硬编码,而是运算出来的。

javawebgetRealPath()方法动态的得到eclipse的安装目录,再拼上配置文件的路径

*/

InputStream ips = new FileInputStream("config.properties");//此处用的是相对路径,不符合开发。

实际开发中不这样放置配置文件,javaweb中通过getRealpath()方法得到eclipse的真实安装完整路径,在拼接上配置文件的内置路径

 

 

2.通过类加载器得到资源文件的方式(不能替代上面的方式,此方式常用)

 

类加载器:

所谓类加载器就是加载类的工具.类加载器也是java类加载器负责读取 Java 字节代码java虚拟机中,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

参考地址:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/

 

 

开发中一般将配置文件放到classpath路径下(用户目录下,即全部存放的是.class文件的文件夹)

配置文件放到当前包里,保存的时候会直接保存在classpath路径下,即用户目录(.class目录)中生成该配置文件。

通过类加载器获取资源文件,只读。

 

格式:

/*1.资源文件放在同个包里,保存的时候eclipse会自动将配置文件放到classpath目录下

通过类加载器加载,在classpath路径指定目录下,逐一查找要加载的文件.

该方式获得资源文件首先要获得class文件,然后获得类加载器,然后才获得资源

* */

InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("com/config.properties");

//2.资源文件放在另一个子包里,通过类文件,直接获得资源文件,用相对路径

InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");

//3.资源文件放在另一个子包里,通过类文件,直接获得资源文件,用绝对路径

InputStream ips = ReflectTest.class.getResourceAsStream("/com/resources/config.properties");

 

 

 

1

public class ReflectPoint {private int x;public int y;public String str1="aa";public String str2="ss";public String str3="dd"; public ReflectPoint(int x, int y) {super();this.x = x;this.y = y;} @Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;} @Overridepublic 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;} //要打印对象的属性值,需覆盖toString()方法public String toString(){return str1+";"+str2+";"+str3;}}


 

2

class  ReflectTest {public static void main(String[] args) throws Exception{//类名.class.getResourceAsStream(String name) :在classpath路径下查找具有给定名称的资源。//类名.class.getClassLoader().getResourceAsStream(String name)在classpath路径下查找,返回读取指定资源的输入流。/*资源文件放在同个包里,保存的时候eclipse会自动将配置文件放到classpath目录下 * 通过类加载器加载,在classpath路径指定目录下,逐一查找要加载的文件. * 该方式获得资源文件首先要获得class文件,然后获得类加载器,然后才获得资源 * */InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("com/config.properties");//资源文件放在另一个子包里,通过类文件,直接获得资源文件,用相对路径//InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");//资源文件放在另一个子包里,通过类文件,直接获得资源文件,用绝对路径//InputStream ips = ReflectTest.class.getResourceAsStream("/com/resources/config.properties");/*资源文件放在project文件夹中, * 要用完整的路径,但完整的路径不是硬编码,而是运算出来的。 * javaweb中getRealPath()方法动态的得到eclipse的安装目录,再拼上配置文件的路径 * *///InputStream ips = new FileInputStream("config.properties");//此处用的是相对路径,不符合开发。Properties pp = new Properties();pp.load(ips);ips.close();//先把widows的widow(系统资源,物理资源)关掉,然后java VM回收本对象String className = pp.getProperty("className");//通过反射,传进一个类名,得到一个默认构造方法生成的对象Collection c = (Collection)Class.forName(className).newInstance();//Collection c = new HashSet();//new ArrayList();//ReflectPoint r1 = new ReflectPoint(2,2);ReflectPoint r2 = new ReflectPoint(3,3);ReflectPoint r3 = new ReflectPoint(2,2);c.add(r1);c.add(r2);c.add(r3);c.add(r1);//r1.y = 8;//c.remove(r1);sop(c.size());}public  static void sop(Object obj)//打印数组或基本数据类型{Class c = obj.getClass();if(c.isArray()){int len = Array.getLength(obj);//通过Array类访问数组,获取数组长度for(int i=0;i<len;i++){System.out.println(Array.get(obj, i));//返回指定数组对象中索引组件的值。}}elseSystem.out.println(obj);}}


 

 

内省引出JavaBean的讲解

内省(IntroSpector)à主要对javabean(特殊的Java类)进行操作

1) 什么是内省?

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的getter/setter方法,这套API就是内省。通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。

 

2) 什么是Javabean?

符合特殊规则的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。方法比较少。这些信息储存在类的私有变量中,通过set()get()获得。

 

3) Javabean的属性:

去掉setget前缀,剩余部分就是属性名,如果剩余部分的第二个字母的小写,则把剩余部分的首字母改成小的。 

总之,一个类被当做javabean使用时,它的属性是根据方法名推断出来的,根本看不到Java类内部的成员变量。

 

4) Javabean的好处?

一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才会去了解和应用JavaBean!好处如下:

Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地!

JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。

 

JavaBean的简单内省操作

操作步骤:

1) 创建javabean对象

2) 创建属性名

3) 描述 Java Bean 通过一对存储器方法导出的一个属性。

4) 获取属性的get,set方法

5) 调用获取的get,set方法

 

 

public class IntroSpectorTest{public static void main(String[] args)throws Exception{//1)创建javabean对象ReflectPoint p1 = new ReflectPoint(66,88);//2)创建属性名String ProName = "x";/*1:PropertyDescriptor pd = new PropertyDescriptor(ProName, p1.getClass());Method m= pd.getReadMethod();Object valX= m.invoke(p1);*///调用重构方法,获取属性值Object valX= getPro(p1,ProName);System.out.println(valX);/*2:Method m1= pd.getWriteMethod();m1.invoke(p1,886);*///调用重构方法,设置属性值setPro(p1,ProName,886);System.out.println(p1.getX());}//将上面的步骤1进行方法重构public static Object getPro(Object obj,String name) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{//3)描述 Java Bean 通过一对存储器方法导出的一个属性。PropertyDescriptor pd = new PropertyDescriptor(name, obj.getClass());//4)获取属性的get方法Method m= pd.getReadMethod();//5)调用获取的get方法Object valX= m.invoke(obj);return valX;}//将上面的步骤2进行方法重构public static void setPro(Object obj,String name,Object value) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{PropertyDescriptor pd = new PropertyDescriptor(name, obj.getClass());//4)获取属性的set方法Method m= pd.getWriteMethod();m.invoke(obj,value);}}


 

JavaBean的复杂内省操作

 

注意:到BeanInfo最好采用“obj.getClass()”方式,而不要采用“类名.class”方式,这样程序更通用。

public static Object getPro(Object obj,String name) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{/*//3)描述 Java Bean 通过一对存储器方法导出的一个属性。PropertyDescriptor pd = new PropertyDescriptor(name, obj.getClass());//4)获取属性的get方法Method m= pd.getReadMethod();//5)调用获取的get方法Object valX= m.invoke(obj);return valX;*///将上面的步骤用复杂操作//在 Java Bean 上进行内省,了解其所有属性、公开的方法和事件。BeanInfo beaninfo = Introspector.getBeanInfo(obj.getClass());//获得 所有beans PropertyDescriptorPropertyDescriptor[] pds = beaninfo.getPropertyDescriptors();Object retVal = null;//遍历beans PropertyDescriptorfor(PropertyDescriptor ps:pds){if(ps.getName().equals(name)){Method m = ps.getReadMethod();retVal = m.invoke(obj);break;}}return retVal;}


 

 

使用BeanUtils工具包操作JavaBean

由上述可看出,内省操作非常的繁琐,所以所以Apache开发了一套简单、易用的API来操作Bean的属性——BeanUtils工具包。

 

BeanUtils工具包:下载:http://commons.apache.org/beanutils/ 注意:应用的时候还需要一个logging包 http://commons.apache.org/logging/

 

1. 获得属性的值,例如,BeanUtils.getProperty(userInfo,"userName"),返回字符串

 

2. 设置属性的值,例如,BeanUtils.setProperty(userInfo,"age",8),参数是字符串或基本类型自动包装。设置属性的值是字符串,获得的值也是字符串,不是基本类型。

 

3. 使用BeanUtils工具类,实现PropertyMap的相互转换

 

4. PropertyUtils工具类

BeanUtils不同在于,运行getPropertysetProperty操作时,没有类型转换,使用属性的原有类型或者包装类。由于age属性的数据类型是int,所以方法PropertyUtils.setProperty(userInfo, "age", "8")会爆出数据类型不匹配,无法将值赋给属性。

 

5. BeanUtils的特点:

1). 对基本数据类型的属性的操作:在WEB开发、使用中,录入和显示时,值会被转换成字符串,但底层运算用的是基本类型,这些类型转到动作由BeanUtils自动完成。

2对引用数据类型的属性的操作:首先在类中必须有对象,不能是null,例如,private Date birthday=new Date();。操作的是对象的属性而不是整个对象,例如,BeanUtils.setProperty(userInfo,"birthday.time",111111);

 

public class IntroSpectorTest{public static void main(String[] args)throws Exception{//1)创建javabean对象ReflectPoint p1 = new ReflectPoint(66,88);//2)创建属性名String ProName = "x";/*1:PropertyDescriptor pd = new PropertyDescriptor(ProName, p1.getClass());Method m= pd.getReadMethod();Object valX= m.invoke(p1);*///调用重构方法,获取属性值Object valX= getPro(p1,ProName);System.out.println(valX);//使用工具类BeanUtils获取属性值,前提是要引入另外一个logging日志开发包,即需要导入两个jar包System.out.println(BeanUtils.getProperty(p1, ProName));/*2:Method m1= pd.getWriteMethod();m1.invoke(p1,886);*///调用重构方法,设置属性值setPro(p1,ProName,886);System.out.println(p1.getX());//1.使用工具类,设置属性值,以字符串的形式操作javabean;BeanUtils.setProperty(p1, ProName, "888");//设置属性的值的类型为字符串,但是该属性的类型为int;可以实现自动类型转换;System.out.println(p1.getX());//2.使用工具类设置属性的属性的值,即属性的级联操作,但是一级属性需要new一个实例BeanUtils.setProperty(p1, "birthday.time", "111");//获取属性的值System.out.println(p1.getBirthday());//获取属性的属性的值System.out.println(BeanUtils.getProperty(p1, "birthday.time"));//3.使用工具类,实现Property和Map的相互转换/*Map map = {name:"xiaoming",age:26};//java 7的新特性BeanUtils.setProperty(map, "name", "lisi");*///4.PropertyUtils工具类PropertyUtils.setProperty(p1, "x", 99);//以属性本身的类型操作javabean;System.out.println(PropertyUtils.getProperty(p1, "x"));}//将上面的步骤1进行方法重构public static Object getPro(Object obj,String name) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{//3)描述 Java Bean 通过一对存储器方法导出的一个属性。PropertyDescriptor pd = new PropertyDescriptor(name, obj.getClass());//4)获取属性的get方法Method m= pd.getReadMethod();//5)调用获取的get方法Object valX= m.invoke(obj);return valX;}//将上面的步骤2进行方法重构public static void setPro(Object obj,String name,Object value) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{PropertyDescriptor pd = new PropertyDescriptor(name, obj.getClass());//4)获取属性的set方法Method m= pd.getWriteMethod();m.invoke(obj,value);}}


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

0 0