黑马程序员—正则表达式+反射
来源:互联网 发布:苹果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字符x 任意字符代表自己本身。
\\ 反斜线字符
\r 回车
\n 换行
B,字符类:
[abc] a,b或c,任意字符一次。
[^abc]任意字符除了a,b或c。
[a-zA-Z] a-z或A-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的值为 true,loader的值为当前类的类加载器。
Class.forName("Foo")等于Class.forName("Foo", true, this.getClass().getClassLoader())
九个预定义Class实例对象
表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,
即 boolean、byte、char、short、int、long、float 和 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类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包、数组等信息。这些信息就是用相应的类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射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.4和JDK1.5的invoke方法的区别
1.4:public Object invoke(Object obj,Object[] args)
将数组作为参数传递给invoke方法,
数组中的每个元素分别对应被调用方法中的一个参数,
所以可写成method.invoke("str",new object[]{1})形式
1.5:public 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[]类型使用
Class的getName()方法:
以 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"
Class的getSuperclass()方法:
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语句创建ArrayList和HashSet的实例对象,
2、然后创建一个properties文件,配置内容: className=java.util.HashSet //className=java.util.ArrayList
3、在程序中加载进配置文件,用配置文件加反射的方式创建ArrayList和HashSet的实例对象
提示
IO流对象.close();的底层含义是:先把widows的widow(系统资源,物理资源)关掉,然后由java VM回收本IO流对象
2) 使用
配置文件的获取方式
1.通过Properties得到资源文件
可以读写配置文件,因为有InputStream和OutputStream
/*资源文件放在project文件夹中,
* 要用完整的路径,但完整的路径不是硬编码,而是运算出来的。
* javaweb中getRealPath()方法动态的得到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的属性:
去掉set和get前缀,剩余部分就是属性名,如果剩余部分的第二个字母的小写,则把剩余部分的首字母改成小的。
总之,一个类被当做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的复杂内省操作
注意:到Bean得Info最好采用“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工具类,实现Property和Map的相互转换
4. PropertyUtils工具类
和BeanUtils不同在于,运行getProperty、setProperty操作时,没有类型转换,使用属性的原有类型或者包装类。由于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>、期待与您交流! -------
- 黑马程序员——反射+正则表达式
- 黑马程序员—正则表达式+反射
- 黑马程序员---正则表达式、反射
- 黑马程序员java学习笔记——正则表达式、反射
- 黑马程序员——反射与正则表达式
- 黑马程序员—java基础之反射与正则表达式
- 黑马程序员--反射与正则表达式
- 黑马程序员-----JAVA 反射、正则表达式
- 黑马程序员--java--反射和正则表达式
- 黑马程序员_正则表达式和反射
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- 黑马程序员—正则表达式
- Unity3D 模型分块
- wireshark过滤语法总结
- Java核心技术第3章(6)
- Kafka深度解析
- iOS真机测试
- 黑马程序员—正则表达式+反射
- CDH5.3 Oozie服务搭建
- 【c和指针学习笔记】深入理解字符串常量
- div模拟弹出框
- python2转3格式自动工具:2to3 , 以及python3相对于python2 修改的一些内容
- 文章标题
- 【c++11并不遥远】使xcode工程支持c++11特性
- SQL Server 2008 R2 清空数据库中ldf日志文件
- PythonChallenge过关斩将录-0~10关