------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
反射这部分内容,是java部分的核心技术,重中之重,所以一定要学好。难点是对动态代理的理解:什么叫反射呢?反射也叫反向加载。我们正常创建一个对象是先new一个实例出来,而反射则是不通过创建实例对象,直接通过.class文件反向加载访问该类中的属性和方法。
获取class对象的三种方式:
1.调用Object类的getClass():任何类都会继承此方法;
2.任何的数据类型(包括基本类型)都有一个:静态的class属性:
Student.class;
int.class
3.调用Class类的静态方法forName(String 全名限定的类名):(常用)
通过这个Class对象,可以获取Student类内部的成员属性、构造方法、成员方法的一些信息,
并能够调用它们;
伪代码:
Student stu = new Student();
Class stuClass1 = stu.getClass();
Class stuClass2 =Student.class;
Class stuClass3 = Class.forName("cn.itcast.demo01_获取Class对象的三种方式.Student");
如何获取构造方法:
获取构造方法,并调用:
Class类:
--批量的:
Constructor[] getConstructors() :获取所有的"公有构造方法"
Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法;
--获取某个构造方法
Constructor getConstructor(Class ... parameterTypes) :获取指定的"公有构造方法";
Constructor getDeclaredConstructor(Class ... parameterTypes) :获取指定的构造方法(包括私有的);
--调用某个构造方法:
Constructor的 Object newInstance(Object... initargs) :调用指定构造方法,并实例化此类的对象;
--暴力访问:如果私有成员,需要设置暴力访问;
Constructor的setAccessible(true):不进行权限检查;
示例:
public class Demo {
public static void main(String[] args) throws Exception {
//1.获取Student的Class对象
Class stuClass = Class.forName("cn.itcast.demo02_通过Class对象获取构造方法并调用.Student");
System.out.println("************获取所有的\"公有构造方法\"*******************");
Constructor[] conArray = stuClass.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************获取所有的构造方法(包括私有的)***************");
conArray = stuClass.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************获取\"公有构造方法\"***********************");
Constructor con = stuClass.getConstructor(String.class);//获取的是"具有String类型形参的"公有构造方法
// Constructor con = stuClass.getConstructor(String.class,int.class);
Object obj = con.newInstance("刘德华");//调用此构造方法,并传递实参:相当于:Object obj = new Student("刘德华");
System.out.println("************获取\"私有构造方法\"并调用*******************");
con = stuClass.getDeclaredConstructor();
//设置暴力访问
con.setAccessible(true);
obj = con.newInstance();
}
}
获取成员属性:
import java.lang.reflect.Field;
/*
* 获取成员属性,并调用:
*
* Class类的方法:
* --批量的:
* Field[] getFields():获取所有"公有属性";
* Field[] getDeclaredFields() :获取所有成员属性(包括私有):
* --获取单个成员属性
* Field getField(String name) :获取指定的"公有属性";
* Field getDeclaredField(String name) :获取指定的属性,包括私有的;
*
* --为成员属性赋值:(注意:1.一定要先实例化此类对象;2.访问私有属性前,要设置暴力访问)
* Field的 void set(Object obj, Object value)
*/
public class Demo {
public static void main(String[] args) throws Exception {
//1.获取Student的Class对象
Class stuClass = Class.forName("cn.itcast.demo03_通过Class对象获取成员属性并调用.Student");
System.out.println("********************获取所有\"公有属性\"************************");
Field[] fieldArray = stuClass.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("********************获取所有属性(包括私有的)************************");
fieldArray = stuClass.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
//创建一个Student对象
Object obj = stuClass.getConstructor().newInstance();
System.out.println("*********************获取公有属性,并调用*********************************");
Field f = stuClass.getField("name");
System.out.println("赋值前打印:" + obj);
f.set(obj, "刘德华");
System.out.println("赋值后打印:" + obj);
System.out.println("*********************获取私有属性,并赋值*********************************");
f = stuClass.getDeclaredField("score");
//暴力访问
f.setAccessible(true);
f.set(obj, 88.5);
System.out.println(obj);
}
}
通过class获取成员方法并调用:
import java.lang.reflect.Method;
/*
* 获取成员方法:
*
* Class类的方法:
* --批量的:
* Method[] getMethods():获取所有"公有方法"(包括继承的)
* Method[] getDeclaredMethods() :获取所有成员方法(包括私有):
* --获取单个成员方法
* Method getMethod(String name, Class... parameterTypes) :获取指定的"公有方法";
* Method getDeclaredMethod(String name, Class... parameterTypes) :获取指定的方法,包括私有的;
*
* --调用方法:(注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问)
* Method:
* Object invoke(Object obj, Object... args) :调用Method所代表的方法:
* 返回值:此Method对象调用所代表的方法,所获取的返回值;
* 形参:obj:方法所属对象;
* args:Method所代表的那个方法的实参列表;
*
*/
public class Demo {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("cn.itcast.demo04_通过Class对象获取成员方法并调用.Student");
System.out.println("*************获取所有\"公有方法\"*******************");
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("**************获取所有的方法(包括私有的)***************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
//创建Student对象
Object obj = stuClass.getConstructor().newInstance();
System.out.println("**************获取公有的,带参的show1()方法,并调用******");
Method m = stuClass.getMethod("show1", String.class,int.class);
Object result = m.invoke(obj, "刘德华",20);//调用show1()方法
System.out.println("调用的返回值:" + result);
System.out.println("***************获取私有的,无参的、无返回值的show4()方法,并调用****************");
m = stuClass.getDeclaredMethod("show4");
m.setAccessible(true);
m.invoke(obj);
}
}
反射的重要应用,操作配置文件:日常的软件中,我们需要更新软件,加入在主方法里应用对象1 ,对象1 更新了,出现了对象2,我们还要在主方法里修改代码,这样不仅费时费力,还浪费资源,怎么可以实现不修改源代码情况下的更新呢?这个时候就可以利用反射机制实现操作配置文件了,配置文件为xxx.ini。里面是键值对形式存在的
A=A1
B=B1
每次程序更新,只要修改配置文件就可以了。利用键的获取值。
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Demo {
public static void main(String[] args) throws Exception {
//1.获取Student的Class对象
// Class stuClass = Class.forName("cn.itcast.demo05_练习_通过反射运行配置文件内容.Student");
String classPath = getValue("className");
Class stuClass = Class.forName(classPath);
//2.实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
//3.调用方法
// Method m = stuClass.getMethod("show");
Method m = stuClass.getMethod(getValue("methodName"));
m.invoke(obj);
}
//一个方法,获取配置文件中的某个键对应的值
public static String getValue(String key) throws IOException{
Properties pro = new Properties();
FileReader in = new FileReader("properties.ini");
pro.load(in);
in.close();
return pro.getProperty(key);
}
}
通过反射越过泛型检查:分析,我们知道,泛型是只存在于编译期的,而反射是直接操作.class文件的,所以,利用java语言的反射机制,可以越过集合的泛型检查。
案例:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/*
* 通过反射越过泛型检查:
*
* 1.定义一个具有String泛型的集合,要求成功向集合中添加一个int数据;
*/
public class Demo {
public static void main(String[] args) throws Exception, SecurityException {
List<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(10);
//通过反射
Class listClass = strList.getClass();
//获取add方法
Method addMethod = listClass.getMethod("add", Object.class);
//调用add方法
addMethod.invoke(strList, 10);
//遍历集合
for(Object o : strList){
System.out.println(o);
}
}
}
练习:通过反射写一个方法,能够设置某个对象的某个属性为指定的值
import java.lang.reflect.Field;
/*
* 通过反射写一个通用的方法,能够设置某个对象的某个属性为指定的值
*/
public class Demo {
public static void main(String[] args) throws SecurityException, Exception {
Cat c = new Cat();
setValue(c, "name", "波斯猫");
c.show();
}
//通过反射写一个通用的设置某个对象的某个属性为指定的值
public static void setValue(Object target,String fieldName,Object value) throws Exception, SecurityException{
//1.获取Class对象
Class aClass = target.getClass();
//2.获取字段
Field f = aClass.getDeclaredField(fieldName);
//3.暴力访问
f.setAccessible(true);
//4.设置
f.set(target, value);
}
}<pre class="java" name="code">public class Cat {
private String name;
public void show(){
System.out.println("name = " + name);
}
}
public class Cat {
private String name;
public void show(){
System.out.println("name = " + name);
}
}
代理模式:代理模式,就是为锁代理的类增加一些额外的操作:
简单实现:
/*
* 此类来代理Student类
* 代理类的目的:是为所代理的类的某些方法,增加一些额外的操作;
*/
public class StudentProxy {
private Student stu = new Student();
public void coding(){
check();
stu.coding();
zongJie();
}
private void check(){
System.out.println("前期检查......");
}
private void zongJie(){
System.out.println("后期总结......");
}
}
public class Student {
public void coding(){
System.out.println("我写程序......");
}
}
public class Demo {
public static void main(String[] args) {
// Student stu = new Student();
StudentProxy stu = new StudentProxy();
stu.coding();
}
}
这样的代理模式,由于每增加一个新产品,都要必须添加一个新的代理类,增加了代码数量,基于此,java反射提供了动态代理机制:
import java.lang.reflect.Proxy;
/*
* Java中实现动态代理:
*
* 步骤:
* 1.自定义类,实现InvocationHandler接口;
* 重写invoke()方法:需要代理的方法,都要经过此方法,
* 我们可以在此方法中做我们想要做的事情;
* 2.动态生成代理对象:
* Proxy类静态方法:newProxyInstance()
*
* 注意:Java的动态代理机制是基于"接口"的,也就是所有需要被代理的类都必须实现某个接口,
* 以达到它们作为共同类型的目的;
*/
public class Demo {
public static void main(String[] args) {
IDAO stuDao = (IDAO)Proxy.newProxyInstance(StudentDAO.class.getClassLoader(),
StudentDAO.class.getInterfaces(),
new MyInvocationHandler(new StudentDAO()));
stuDao.save();
stuDao.update();
IDAO teaDao = (IDAO)Proxy.newProxyInstance(TeacherDAO.class.getClassLoader(),
TeacherDAO.class.getInterfaces(),
new MyInvocationHandler(new TeacherDAO()));
teaDao.save();
teaDao.update();
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
check();
Object result = method.invoke(target);//这一步才是真正调用被代理对象中的方法;
log();
return result;
}
public void check(){
System.out.println("权限检查......");
}
public void log(){
System.out.println("记录日志......");
}
}
模板设计:解决子类重写方法时候权限过大的问题:
/*
* 模版设计模式概述和使用
*
*
*/
abstract class Person{
public void show(){//模板方法
System.out.println("大家好,我是黑马的一名 " + getType() + "!");
}
public abstract String getType();
}
class Student extends Person{
public String getType(){
return "学生";
}
}
class Teacher extends Person{
public String getType(){
return "教师";
}
}
public class Demo {
public static void main(String[] args) {
Student stu = new Student();
stu.show();
Teacher tea = new Teacher();
tea.show();
}
}
装饰设计:代码量过大,不写代码了,简单说明一下,装饰设计模式,和代理模式很像,但又不是代理模式,装饰模式的结构图如下:
枚举实际上是基于多利模式的,就比如拿颜色来举例:
自己实现枚举类:
1.多例模式:某些类在整个应用程序运行期间,只需要几个对象,不能多,有时也不能少;
例如:扑克类--只能有54个对象;
骰子类--只能有2个对象;
2.枚举,就是基于"多例模式";
3.例子:我们的程序需要颜色类,我们定义一个类MyColor。在整个应用程序运行期间,只需要
三个对象:红,绿,蓝
4.枚举的实现方式:
1).定义MyColor类,不能让其它类任意的实例化此类对象--构造方法私有化
枚举的关键字:enum;
/*
* 通过enum实现枚举类
*
* 1.enum是Java中第一个关键字,用于定义"枚举";
*
* 枚举的注意事项:
* 定义枚举类要用关键字enum:
所有枚举类都是Enum的子类
枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
枚举类可以有构造器,但必须是private的,它默认的也是private的。枚举项的用法比较特殊:枚举(“”);
枚举类也可以有抽象方法,但是枚举项必须重写该方法
*/
public class Demo {
public static void main(String[] args) {
MyColor redColor = MyColor.RED;
System.out.println(redColor);
redColor.show();
}
}
public enum MyColor {
RED("红"){
@Override
public void show() {
System.out.println("我是:" + this.getName() + " 色的!");
}
},GREEN("绿"){
@Override
public void show() {
System.out.println("我是:" + this.getName() + " 色的");
}
},BLUE("蓝"){
@Override
public void show() {
System.out.println("我是:" + this.getName() + " 色的");
}
};
private String name;
private MyColor(String name){
this.name = name;
}
public String getName(){
return this.name;
}
//包含抽象方法
public abstract void show();
//重写toString
@Override
public String toString() {
return "MyColor [name = " + this.name + "]";
}
}
枚举中常用的一些方法:
/*
* 枚举中常用的方法:
*
* int compareTo(E o):比较的是序号;
String name():返回枚举的名称;
int ordinal():获取序号
String toString():默认打印枚举名称,可以重写;
<T> T valueOf(Class<T> type,String name):将字符串转换为某个枚举类型;
values() :将枚举转换为数组;
*/
public class Demo {
public static void main(String[] args) {
MyColor red = MyColor.RED;
MyColor green = MyColor.GREEN;
MyColor blue = MyColor.BLUE;
System.out.println(red.compareTo(green));
System.out.println(red.compareTo(blue));
System.out.println(blue.compareTo(red));
System.out.println(green.compareTo(red));
System.out.println("red.name():" + red.name());
System.out.println(red.ordinal());
System.out.println(green.ordinal());
System.out.println(blue.ordinal());
MyColor c = MyColor.valueOf(MyColor.class, "BLUE");
System.out.println(c);
System.out.println("将枚举转换为数组:");
MyColor[] colorArray = MyColor.values();
for(MyColor cc : colorArray){
System.out.println(cc);
}
}
}
java基础知识新特性回归:
到这里,所有的java基础知识都已经学完了,jdk5,jdk7都有一些新的特性,现在进行一下新特性总结:
jdk5新特性:
import java.util.ArrayList;
import java.util.List;
/*
* JDK5的新特性:
* 1.自动装箱和拆箱
2.泛型
3.增强for循环
4.静态导入
5.可变参数
6.枚举
*/
//import static java.lang.Math.max;
import static java.lang.Math.*;
public class Demo {
public static void main(String[] args) {
//1.自动装箱和拆箱
Integer intObj = 10;//自动装箱
int n = intObj;//自动拆箱
intObj++;//先拆箱,后装箱
//2.泛型
List<String> strList = new ArrayList<>();
strList.add("aaa");
// strList.add(10);//编译错误
//3.增强for
int[] intArray = {1,32,432,54,41,41};
for(int n2 : intArray){
System.out.println(n2);
}
//4.静态导入:
System.out.println("求10,20的最大值:" + max(10, 20));
//5.可变参数
show();//可以不传参数
show(1,432,43,25,3424,423,342,432,43,243,24,325,43654,76,54);
}
//可变参数
public static void show(int ... nums){
for(int n : nums){
System.out.println(n);
}
}
}
jdk7的新特性:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/*
* JDK7的六个新特性回顾:
*
* 1.二进制字面量
2.数字字面量可以出现下划线
3.switch 语句可以用字符串
4.泛型简化
5.异常的多个catch合并
6.try-with-resources 语句
*/
public class Demo {
public static void main(String[] args) {
//1.二进制字面量
System.out.println(10);//十进制
System.out.println(0b1010);//二进制
System.out.println(012);//八进制
System.out.println(0xA);//十六进制
//2.数字字面量可以出现下划线
double n = 1_2_3_4_5_6.7_8_9;
System.out.println(n);
//3.switch 语句可以用字符串
String str = "JAVA";
switch(str){
case "Java":
case "JAVA":
case "java":
System.out.println("欢迎学习Java");
break;
case "C++":
System.out.println("欢迎学习C++");
break;
case "C#":
System.out.println("欢迎学习C#");
break;
}
//4.泛型简化
List<String> list = new ArrayList<>();
//5.异常的多个catch合并
try{
}catch(ArrayIndexOutOfBoundsException | NullPointerException | ArithmeticException 变量名){
}catch(Exception e){
}
//6.try-with-resources 语句
try(
FileInputStream in = new FileInputStream("demo17.txt");
){
}catch(FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
以上到此,就是全部的java基础的主要知识,非常期待能够进入黑马训练营继续学习编程!