反射(解剖)技术
-
其他技术
-
MD5加密技术
-
MD5它是一种算法,它可以对普通字符串、数字、文件等进行加密。得到的加密后的数据(密文)是无法被破解的。
MD5也被称为单向不可逆的算法。
明文 算法 密文
"abc" -----à MD5算法 ------à "123384734584823445899"
使用类中静态的方法获取类的对象:
/*
* 演示 使用 MessageDigest 对数据进行加密
*/
public class MD5Utils {
/*
* getMD5 方法是对传递进来的字符串使用MD5算法加密
*/
public static String getMD5(String value) throws NoSuchAlgorithmException {
// 得到加密的对象
MessageDigest digest = MessageDigest.getInstance("MD5");
/*
* 使用对象对字符串进行加密 byte[] digest(byte[] input)
* 方法接收的一个字节数组,被加密的明文数据,返回的字节数组就是加密后的密文数据
*/
byte[] bs = digest.digest(value.getBytes());
// 定一个字符串缓冲区,用于存储转后的数据
StringBuilder sb = new StringBuilder();
// 需要对加密后的数组中的密文数据进行处理,将数据转成字符串,返回给调用者
for (byte b : bs) {
/*
* 需要对加密后的byte类型的数据转成十六进制,加密的byte数据的范围-128~127之间的数字,
* 而这些数字恰好占用8个二进制数位,如果我们取出8个二进制数位之后,将其转成int值,那么就会在高位
* 补足24个零。当高位补零之后,所有的数字都会转成正数。
*/
int x = b & 255;
// 将int值转成十六进制数据
String s = Integer.toHexString(x);
// 需要将转后的数据保存到字符串缓冲区中
if( x >=0 && x <= 15 ){
sb.append("0");
sb.append(s);
}else{
sb.append(s);
}
}
return sb.toString();
}
public static void main(String[] args) throws NoSuchAlgorithmException {
System.out.println(getMD5("a你"));
}
}
-
Junit测试(练习)
IDE:集成开发环境。我们开发软件的时候使用的编辑代码的编辑器(开发工具)。
测试:它是软件开发中的一个环节。开发的软件在上线之前必须经过测试,找到软件开发中的一些问题。
测试分成两种:
白盒测试:在测试软件的时候,关注的是软件中的代码的流程,代码之间的调用关系等,不关注代码所实现的功能是否符合实际需求。
黑盒测试:它是指软件开发环节结束,进行功能测试环节,主要是面对软件实现的功能测试,不关注代码。只要功能符合需求即可。
任何高级的开发工具,它们都自带了测试代码的工具:eclipse自带的测试工具Junit。
eclipse自带的junit测试使用步骤:
1、在需要被测试的方法添加@Test 注解
2、在报错的地方使用ctrl + 1 提示,根据提示导入junit的jar包
添加完成之后,在项目目录下会多出一个junit的jar包
3、选中添加@Test的方法,右击选择 junit 运行
在运行之后,会出现junit的视图:
junit的视图如果显示的绿条,表示被测试的方法没有问题。如果是红色条,测试失败。
junit测试的细节:
1、@Test它只能添加在public修饰的、没有返回值的、不需要参数的方法上。
2、其他的注解:
@Before 它是在添加@Test方法运行之前运行
@After 它是在添加@Test方法运行之后运行
@BeforeClass 它是在类加载的时候运行,类似于静态代码块的作用
@AfterClass 它是在程序结束的时候运行
-
debug调试(练习)
debug它是开发工具自带的调试程序的功能。
需求:计算1到10和值,要求打印出sum在计算累加和时的变化情况。
debug的调试使用步骤:
1、在需要查询变量、调试的表达式、语句最左侧双击打上断点
2、运行程序,选择debug as 方式运行
3、在确认框中选择yes,引入到debug的调试视图中
4、选择对应的符号,进行程序的调试动作
F8 跳转到程序中的下一个断点位置。如果程序中只有一个断点,这时程序会运行结束。
停止JVM的运行
F6 它是跳转到程序停留所在行的下一行。
F5 跳转到当前程序停留所在行调用的方法中。
F7 跳出进入的方法
5、在调试的过程中,我们可以使用watch 功能,查询某个表达式、变量、语句的结果
6、调试结束之后,需要操作两个步骤:
1)清空所有的表达式视图中监控的变量、表达式信息。
2)清空程序中所有的断点。
7、调试结束之后,将视图切换到当初开发时候使用的视图。
-
枚举介绍(了解)
-
枚举的介绍
-
枚举:它是本身是Java在JDK5的时候给出的一个新的数据类型。但这个类型有自己特定的应用场景。
单例类:它是保证类的对象唯一的。
多例类:一个类的对象是有限个。这时这个类就不能提供公开的构造方法,必须像单例类的书写一样,在类中将所有可以存在的对象创建出来,然后对外提供对应的方法获取对象。
/*
* 演示多例类
*/
public class Sex {
// 私有构造方法
private Sex(){}
// 创建对象
private static Sex male = new Sex();
private static Sex female = new Sex();
// 提供方法
public static Sex getMale(){
return male;
}
public static Sex getFemale(){
return female;
}
}
上面的代码演示一个简单的多例类,而类中的对象个数是固定的,因此如果我们程序中需要多例类的话,可以使用JDK5中枚举技术代替多例类。
枚举类的定义:
修饰符 enum 类名{
}
/*
* 定义枚举类
*/
public enum Gender {
// 必须是定义这个类可以出现的多个对象的引用变量名
male , female;
}
-
带有参数和成员变量的枚举类
/*
* 定义枚举类
*/
public enum Gender {
// 必须是定义这个类可以出现的多个对象的引用变量名
male("男") , female("女");
// 定义成员变量
private String name;
// 提供有参数的构造方法
private Gender( String name ){
this.name = name;
}
}
-
拥有抽象方法的枚举类
/*
* 定义枚举类
*/
public enum Gender {
// 必须是定义这个类可以出现的多个对象的引用变量名
male("男"){
public void print(){
System.out.println("男");
}
} ,
female("女"){
public void print(){
System.out.println("女");
}
};
// 定义成员变量
private String name;
// 提供有参数的构造方法
private Gender( String name ){
this.name = name;
}
// 书写抽象方法
public abstract void print();
}
-
枚举类的细节
-
只要定义好枚举类之后,这个类中就默认有个私有无参数的构造方法
-
枚举类中的第一行,必须是定义这个枚举类可以出现的那些对应的引用变量名称。
-
所有枚举类默认的父类都是Enum类,但它可以可以使用implements 实现接口
-
自己定义的枚举类,可以直接使用Enum类中的方法
-
面试题:
switch语句都支持哪些数据类型?
在JDK5之前支持4种基本类型:byte、short、int、char
在JDK5的时候支持:enum(枚举)类型
在JDK7的时候支持:String(字符串)类型
-
反射(解剖)的应用场景
-
Class介绍
-
Class对象介绍 理解
-
我们在程序中书写的接口、抽象类、类、内部类、枚举等它们在使用javac编译之后生成都是class文件(字节码文件)。而所有的class文件对JVM而言就是一类可以被执行运行的文件。class文件可以看成一类特殊的文件数据。
在Java中使用Class类专门负责描述任何的class文件。
-
获取Class对象方式 必须掌握
Class类,它描述的任何的class文件(字节码文件)。任何的class文件它们都是Class类的一个实例。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
-
第一种方式(重点)
我们通过new关键字创建出的任何类的对象它们都是基于某个class文件在对象中的Class对象创建出来的。我们就可以通过new出来的这个类的就一个具体的对象找到它所依赖的那个类对应的class文件对象。
/*
* 演示获取Class对象(某个类对应的class文件)的第一种方式:
* 使用Object类中的getClass方法,由于任何使用new关键字创建出来的对象
* 它们都有能力找到创建自己时依赖的class文件
*/
@Test
public void demo1(){
// 创建Person的对象
Person p = new Person();
// 使用Object类中的getClass方法
Class clazz = p.getClass();
System.out.println(clazz);
// 获取数组对应的Class的对象(数组对应的class文件)
int[] arr = new int[4];
System.out.println(arr);
Class clazz2 = arr.getClass();
System.out.println(clazz2);
}
-
第二种方式(重点)
第一种获取Class对象的方法是必须得到某个类型对应的具体的对象,然后通过对象调用getClass方法得到class文件。平时我们开发中,如果得到某个类的对象,这时就可以通过这个对象调用类中的方法完成需求,没有必要反推得到class文件。
第二种方案:
任何类型都可以直接调用class属性,得到其对应的class文件(Class对象)。
格式:类型.class
/*
* 任何类型其中都一个class属性
*/
@Test
public void demo2(){
// 获取自定义类型对应的clas文件
Class clazz = Person.class;
System.out.println(clazz);
// 获取数组对应的class文件
Class clazz2 = int[].class;
System.out.println(clazz2);
// 基本类型
Class clazz3 = int.class;
System.out.println(clazz3);
Class clazz4 = java.util.List.class;
System.out.println(clazz4);
}
-
第三种方式(重点)
第二种方式要求必须知道具体的类型,才能获取到class文件。平时我们在获取Class对象(class文件)的时候,并不一定会知道具体的数据类型。
真正在写程序的时候,我们需要通过文件读取别人配置的一些相关类的信息,这时别人配置什么,我们的程序就会读取到什么。读取到具体的信息后,才能通过读取的数据加载和获取对应的class文件。
/*
* 介绍第三种方式获取Class对象
* 在Class类中有个forName方法,可以将需要加载的类或接口的信息以字符串的形式传递给forName方法
* 这个方法就能够将硬盘的class文件找到并加载到内存中
*
* 使用Class类中的forName方法加载某个class文件,这时要求书写的字符串必须的包名和类名
*/
@Test
public void demo3() throws ClassNotFoundException{
// 字符串表示的需要被加载的类的信息 ,这里的字符串信息未来是从文件中读取的
String classname = "cn.itcast.sh.b_class.Person";
/*
* 使用Class类中的静态方法加载
* 在我们使用Class类中的forName方法的时候,它的底层其实是将读取到的字符串表示的类或接口的信息对应的class文件
* 从硬件上加载的方法区中,并在堆中创建出Class的对象,最后将Class对象的内存地址赋值clazz变量
*/
Class clazz = Class.forName(classname);
System.out.println(clazz);
}
-
创建class文件对应的类的对象
/*
* 使用Class类中的newInstance方法创建某个类的对象
*/
@Test
public void demo4() throws Exception{
// 获取Class的对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
// 调用newInstance方法得到某个类的对象
Object obj = clazz.newInstance();
// 上面两行代码等价于 Person p = new Person();
System.out.println(obj);
}
注意:
Class类中的newInstance方法要求被反射的类中必须有公开的空参数的构造方法。否则使用newInstance方法就发生异常。
-
反射(解剖、解析)类的成员
-
反射成员介绍 (重点)☆☆☆☆☆
-
在我们定义类的时候,类中可以书写成员变量、成员方法、构造方法。编译源代码之后生成的class文件中肯定有这些成员内容。
-
反射构造方法 (重点)☆☆☆☆☆
一个类中可以有多个构造方法,它们是以重载的形式存在。
类中多个构造方法怎么区分:
通过参数列表区分。其实是根据参数列表中变量的具体的数据类型区分。
-
反射非私有构造方法(重点)
/*
* 反射类中非私有的构造方法
* public Person(String name, int age)
*/
@Test
public void demo1() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
/*
* 反射构造方法
* getConstructor(Class<?>... parameterTypes)
* 参数解释:
* Class<?>... parameterTypes : 被反射的构造方法上参数类型对应的Class
*/
Constructor cons = clazz.getConstructor( String.class , int.class );
/*
* 反射到类中的构造方法之后,需要使用Constructor类中的newInstance方法创建这个类的对象
* newInstance(Object... initargs)
* 参数解释:
* Object... initargs : 被反射的构造方法在运行的时候需要的实际参数
*/
Object obj = cons.newInstance("班长",18);
// 上面三行代码等价于 Person p = new Person("班长",18);
System.out.println(obj);
}
-
反射私有构造方法(重点)
/*
* 反射类中的私有的构造方法
* private Person(String name)
*/
@Test
public void demo2() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
// 反射私有构造方法
// getDeclaredConstructor(Class<?>... parameterTypes)
Constructor cons = clazz.getDeclaredConstructor(String.class);
// 如果要访问类中私有的成员,这时必须强制取消Java的权限检查(暴力访问)
cons.setAccessible(true);
// 调用newInstance方法得到对象
Object obj = cons.newInstance("经纪人");
System.out.println(obj);
}
-
获取所有构造方法
/*
* 获取类中所有构造方法
*/
@Test
public void demo3() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
/*
* getConstructors 获取到的是类所有的公开的构造方法
*/
Constructor[] cons = clazz.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("------------------------");
/*
* getDeclaredConstructors 获取到的是类中所有的构造方法(公开和私有)
*/
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(con);
}
}
面试题:private的所用:
private是修饰类中的成员。被private修饰的成员,只能在本类中访问。但是如果我们使用反射技术,通过取消权限检查,私用的成员,在本类以外的其他地方也可以被使用。
-
反射成员变量 ☆☆☆☆☆
成员变量如何区分:
类中的成员变量只能通过变量名区分。
-
反射非私有非静态成员变量(重点)
/*
* 反射类中公开非静态的成员变量
*/
@Test
public void demo1() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
/*
* getField(String name)
* String name : 被反射的变量名
*
* public String addr
*/
Field field = clazz.getField( "addr" );
/*
* 类中的非静态的成员变量,它如果要能够存在,必须依赖在当前类的某个对象上。
* 我们在使用反射的成员变量之前,需要得到当前被反射的这个类的一个对象
*/
Object obj = clazz.newInstance();
/*
* 反射到成员变量,仅仅只能做两件事:
* 1、给成员变量赋值
* 2、获取成员变量的值
*
* set(Object obj, Object value)
* Object obj : 成员变量锁依赖的对象,如果成员变量是静态的,这个参数可以书写null
* Object value : 它是给成员变量设置的值
*/
field.set(obj, "北京");
System.out.println(obj);
}
-
反射私有静态成员变量(重点)
/*
* 反射私有静态成员变量
*
* 静态成员变量,它不需要依赖类的对象
* private static String sex;
*/
@Test
public void demo2() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
Field field = clazz.getDeclaredField("sex");
// 由于是静态的,不需要对象,由于是私有的,需要取消权限
field.setAccessible(true);
field.set(null, "男");
// 在设置值之后,创建对象,通过对象的打印,看反射的静态的值是否可以给当前这个对象使用
Object obj = clazz.newInstance();
System.out.println(obj);
}
-
获取类中的所有成员变量
/*
* 获取类中的所有成员变量
*/
@Test
public void demo3() throws Exception{
Class clazz = Class.forName("cn.itcast.sh.b_class.Student");
/*
* getFields 获取到的包含父类中的所有公开的成员变量
*/
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-----------------------------");
/*
* getDeclaredFields 获取到的是本类中的所有成员变量
*/
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
}
-
反射成员方法 ☆☆☆☆☆
成员方法怎么区分:
需要根据方法的名称和参数列表的参数类型一起区分。
-
反射非私有非静态的有参数没有返回值成员方法(重点)
/*
* 反射非私有非静态的有参数没有返回值成员方法
* public void setName(String name)
*/
@Test
public void demo1() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
/*
* 反射成员方法
* getMethod(String name, Class<?>... parameterTypes)
* String name : 被反射的方法名
* Class<?>... parameterTypes : 方法的参数列表对应的类型的class文件
*/
Method method = clazz.getMethod("setName", String.class);
/*
* 由于反射的方法是非静态,因此让被反射的方法运行,必须有对象
*/
Object obj = clazz.newInstance();
/*
* 反射到类中的成员方法之后,会得到Method对象,如果让被反射的方法运行,必须调用Method中的invoke方法
* Object invoke(Object obj, Object... args)
* 参数解释:
* Object obj :被反射的方法运行的时候依赖的那个对象
* Object... args : 被反射的方法运行的时候需要的实际参数
* invoke方法的返回值Object,其实是被反射的方法的返回值
*/
Object value = method.invoke(obj, "秋香");
System.out.println(value);
System.out.println(obj);
}
-
反射类中私有静态的无参数没有返回值成员方法(重点)
/*
*反射类中私有静态的无参数没有返回值成员方法
*private static void demo()
*/
@Test
public void demo2() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
// 由于被反射的方法没有参数,所有反射的时候参数书写null
Method method = clazz.getDeclaredMethod("demo", null);
// 由于方法是静态的,不需要对象,私有的需要取消权限
method.setAccessible(true);
method.invoke(null, null);
}
-
反射私有非静态有参数有返回的方法
/*
* 反射类中私有,非静态,有参数,有返回值的方法
*/
@Test
public void demo3() throws Exception{
// 获取Class对象
Class clazz = Class.forName("cn.itcast.sh.b_class.Person");
// 反射 private int add(int a , int b)
Method method = clazz.getDeclaredMethod("add", int.class , int.class);
// 私有需要取消权限
method.setAccessible(true);
// 由于非静态,需要对象
Object obj = clazz.newInstance();
// 调用invoke 让方法运行
Object value = method.invoke(obj, 1,3);
System.out.println(value);
}
-
反射中需要掌握
1、理解Class到底是干什么的?
2、必须掌握获取Class对象三种方式
3、反射类中的成员变量、成员方法、构造方法,注意私有的反射需要取消权限
反射后期会结合 动态代理 技术一起使用。
-
动态代理介绍
-
代理类
/*
* 代理类 (经纪人)
* 在Java中,如果要对外提供代理类的对象,必须使用Java中的Proxy类来产生代理对象。
*
* 使用Proxy类中的newProxyInstance方法 获取代理类对象
* static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* ClassLoader loader:类加载器。
* Class<?>[] interfaces : 被代理类实现的所有接口
* InvocationHandler h : InvocationHandler 它主要的作用是在拦截外界调用的任何方法
* 当外界调用了代理对象的任何方法时,都被拦截之后,会直接去执行接口中的invoke方法
*/
public class ProxyClass {
//实例化,被代理类对象
private static final SongXiaoBao sxb = new SongXiaoBao();
/*
* 在代理类中书写一个静态方法,对外提供代理类对象
*/
public static Inter getProxy(){
//使用Proxy类获取到一个代理对象
Inter obj = (Inter)Proxy.newProxyInstance(
//指定类加载器
ProxyClass.class.getClassLoader(),
//指定被代理类实现的所有接口
sxb.getClass().getInterfaces(),
//指定专门用于拦截的接口
new InvocationHandler(){
/*
* 当InvocationHandler 拦截到任何的方法被执行时,自动的运行接口中的invoke方法
* 解释invoke方法上的三个参数
* Object proxy:代理类对象
* Method method:被外界调用的方法,已经封装成Method对象
* Object[] args:被调用方法接收的实际参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 拦截到之后,在invoke方法中可以判断当前调用的具体是什么方法
* 然后在用反射的方式调用被代理的方法
*/
if( "sing".equals(method.getName()) ){
System.out.println("先交钱,再服务.....");
//反射被代理类中的sing方法
return method.invoke(sxb, args);
}else if("action".equals(method.getName())){
System.out.println("先准备一个搭档,必须女滴.....");
//反射被代理类中的action方法
return method.invoke(sxb, args);
}else{
return "暂时本经纪人不代理明星此项业务";
}
}
});
//返回代理对象
return obj;
}
}
-
被代理类
/*
* 被代理类 ,代理类实现的接口中的所有方法全部可以被代理
*/
public class SongXiaoBao implements Inter {
@Override
public String sing(String name){
System.out.println("宋小宝唱《"+name+"》歌");
return "损色";
}
@Override
public void action(){
System.out.println("宋小宝表演二人转");
}
public void sleep(){
System.out.println("宋小宝休息");
}
}
-
被代理实现的接口
/*
* 在Java中规定,只有保存在接口中的方法才能被代理。
*/
public interface Inter {
public String sing(String name);
void action();
}
-
测试类
/*
* 测试类
*/
public class TestProxy {
public static void main(String[] args) {
//获取到代理类对象(经纪人对象)
Inter obj = ProxyClass.getProxy();
//通过代理对象,实现调用被代理类中的方法
String value = obj.sing("海燕");
System.out.println(value);
obj.action();
Object v = obj.toString();
System.out.println(v);
}
}