<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:什么是Java反射?它的應(yīng)用場(chǎng)景有哪些?

          共 13756字,需瀏覽 28分鐘

           ·

          2021-03-14 06:06

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來(lái),我們一起精進(jìn)!你不來(lái),我和你的競(jìng)爭(zhēng)對(duì)手一起精進(jìn)!

          編輯:業(yè)余草

          javazhiyin.com/34563.html

          推薦:https://www.xttblog.com/?p=5165

          什么是反射


          反射就是指程序在運(yùn)行的時(shí)候可以知道一個(gè)類(lèi)的自身信息。


          對(duì)于任何一個(gè)類(lèi):可以知道這個(gè)類(lèi)的屬性和方法。


          對(duì)于任何一個(gè)對(duì)象:可以調(diào)用這個(gè)對(duì)象的任何一個(gè)方法和屬性。


          反射就是把java類(lèi)中的各種成分映射成一個(gè)個(gè)的Java對(duì)象


          例如:一個(gè)類(lèi)有:成員變量、方法、構(gòu)造方法、包等等信息,利用反射技術(shù)可以對(duì)一個(gè)類(lèi)進(jìn)行 解剖,把個(gè)個(gè) 組成部分映射成一個(gè)個(gè)對(duì)象。


          (其實(shí):一個(gè)類(lèi)中這些成員方法、構(gòu)造方法、在加入類(lèi)中都有一個(gè)類(lèi)來(lái)描述)


          反射的功能


          • 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類(lèi)

          • 在運(yùn)行的時(shí)候構(gòu)造任意一個(gè)類(lèi)的對(duì)象

          • 在運(yùn)行時(shí)判斷一個(gè)類(lèi)所具有的成員變量和方法

          • 在運(yùn)行時(shí)調(diào)用任何一個(gè)對(duì)象的方法

          • 生成動(dòng)態(tài)代理(會(huì)有一篇關(guān)于動(dòng)態(tài)代理的文章,在這里挖坑)


          反射的優(yōu)點(diǎn)和缺點(diǎn)


          動(dòng)態(tài)編譯和靜態(tài)編譯


          反射用到的是動(dòng)態(tài)編譯,既然有動(dòng)態(tài)編譯,就會(huì)有靜態(tài)編譯


          那么動(dòng)態(tài)編譯和靜態(tài)編譯又有什么區(qū)別?


          靜態(tài)編譯:在編譯的時(shí)候進(jìn)確定類(lèi)型,如果綁定對(duì)象成功,new 是靜態(tài)加載類(lèi),就編譯通過(guò)。


          1 代碼示例


          public class Phone{
          public static void main(String[] args){
          if("iphone".equals(args[0])){
          Iphone iphone = new Iphone();
          iphone.call();
          }
          if("xiaomi".equals(args[0])){
          Xiaomi xiaomi = new Xiaomi();
          xiaomi.call();
          }
          }
          }
          class Xiaomi{
          public void call(){
          System.out.println("xiaomi is calling");
          }
          }
          class Iphone{
          public void call(){
          System.out.println("iphone is calling");
          }
          }


          2 解釋


          當(dāng)在Phone.java里面寫(xiě)好代碼的時(shí)候,如果需要添加新的類(lèi),則需要直接在文件里面修改代碼。假如需要添加一個(gè)華為手機(jī),則我需要在Phone.java文件里面加個(gè)if語(yǔ)句判斷傳進(jìn)來(lái)的參數(shù)是不是"huawei",這樣增加了類(lèi)之間的耦合性。


          當(dāng)刪除一個(gè)類(lèi)的時(shí)候Phone.java編譯可能會(huì)出現(xiàn)錯(cuò)誤。  假如我刪除了小米手機(jī)這個(gè)類(lèi),phone.java文件沒(méi)有刪除if判斷語(yǔ)句,那么phone.java在編譯的時(shí)候則會(huì)失敗。


          沒(méi)刪除Xiaomi.java編譯的時(shí)候是成功并且成功運(yùn)行


          刪除Xiaomi.java編譯的時(shí)候就會(huì)失敗了,因?yàn)閄iaomi.java不存在


          動(dòng)態(tài)編譯:在運(yùn)行的時(shí)候確定類(lèi)型,綁定對(duì)象。最大發(fā)揮了Java的多態(tài),降低類(lèi)之間的耦合性。


          1 代碼示例


          Phone.java


          public static void main(String[] args){
          try{
          Class c = Class.forName("Huawei");
          PhoneInterface cellPhone = (PhoneInterface)c.newInstance();
          cellPhone.ring();
          }catch (Exception e){
          e.printStackTrace();
          }
          }
          PhoneInterface.java
          interface PhoneInterface{
          void ring();
          }
          Huawei.java
          public class Huawei implements PhoneInterface{
          @Override
          public void ring(){
          System.out.println("huawei is ringing...");
          }
          }
          OnePlus.java
          public class OnePlus implements PhoneInterface{
          @Override
          public void ring(){
          System.out.println("OnePlus is ringing...");
          }
          }


          2 解釋


          (1)對(duì)比靜態(tài)編譯,當(dāng)我們需要往Phone.java里面?zhèn)鬟f新的類(lèi)參數(shù)的時(shí)候,根本不需要修改Phone.java的代碼,因?yàn)檫@里應(yīng)用了Java的多態(tài)。只要新建一個(gè)新的類(lèi)實(shí)現(xiàn)了PhoneInterface的接口,把類(lèi)名傳進(jìn)去就可以調(diào)用。這里體現(xiàn)了 需要哪個(gè)類(lèi)的對(duì)象就動(dòng)態(tài)的創(chuàng)建哪個(gè)類(lèi)的對(duì)象,也就是說(shuō)動(dòng)態(tài)的實(shí)現(xiàn)了類(lèi)的加載

          (2)當(dāng)刪除一個(gè)類(lèi)的時(shí)候,Phone.java文件不會(huì)編譯失敗。


          比如說(shuō)刪除OnePlus.java


          區(qū)別:這里說(shuō)明了動(dòng)態(tài)加載的在不修改Phone.java的前提下不會(huì)因?yàn)槠渌?lèi)的不存在而導(dǎo)致整個(gè)文件不能編譯,而靜態(tài)加載則會(huì)編譯的時(shí)候綁定對(duì)象,從而導(dǎo)致編譯失敗。


          優(yōu)點(diǎn)


          以實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建對(duì)象和編譯,體現(xiàn)出很大的靈活性,特別是在J2EE的開(kāi)發(fā)中它的靈活性就表現(xiàn)的十分明顯。比如,一個(gè)大型的軟件,不可能一次就把把它設(shè)計(jì)的很完美,當(dāng)這個(gè)程序編譯后,發(fā)布了,當(dāng)發(fā)現(xiàn)需要更新某些功能時(shí),我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個(gè)軟件肯定是沒(méi)有多少人用的。采用靜態(tài)的話,需要把整個(gè)程序重新編譯一次才可以實(shí)現(xiàn)功能的更新,而采用反射機(jī)制的話,它就可以不用卸載,只需要在運(yùn)行時(shí)才動(dòng)態(tài)的創(chuàng)建和編譯,就可以實(shí)現(xiàn)該功能。


          缺點(diǎn)


          對(duì)性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類(lèi)操作總是慢于只直接執(zhí)行相同的操作。


          Class類(lèi)和類(lèi)類(lèi)型


          Class類(lèi)


          所有的類(lèi)是java.lang.Class類(lèi)的對(duì)象,Class類(lèi)是所有類(lèi)的類(lèi),反射的基礎(chǔ)。


          Class對(duì)象(類(lèi)類(lèi)型)


          普通類(lèi)構(gòu)造對(duì)象是:Student s = new Student();


          但Class對(duì)象則不是,看Class類(lèi)的源碼,構(gòu)造器是私有的,則用戶無(wú)法直接像普通類(lèi)那樣new一個(gè)Class的對(duì)象,只有JVM才可以構(gòu)造Class對(duì)象


          private Class(ClassLoader loader) {
          // Initialize final field for classLoader. The initialization value of non-null
          // prevents future JIT optimizations from assuming this final field is null.
          classLoader = loader;
          }


          但是我們可以通過(guò)一個(gè)已知的類(lèi)獲得Class對(duì)象


          有以下三種方式:


          Class s = Student.class;
          Class s1 = new Student().getClass();
          Class s2 = Class.forName("Student");


          Class對(duì)象就是類(lèi)類(lèi)型,在這里表示的是Student類(lèi)的類(lèi)型,下面看一個(gè)圖了解Class對(duì)象是什么和類(lèi)在JVM中加載的過(guò)程

          由圖中可以看出,一個(gè)具體的Class對(duì)象就保存了具體類(lèi)的基本屬性和方法,并且可以調(diào)用。


          Student類(lèi)


          package General;

          import java.lang.reflect.Method;

          public class Student {
          private String name;
          private int age;
          private String msg = "I am a student";

          public void fun() {
          System.out.println("fun");
          }

          public void fun(String name,int age) {
          System.out.println("我叫"+name+",今年"+age+"歲");
          }

          public Student(){

          }

          private Student(String name){

          }

          public String getName() {
          return name;
          }

          public void setName(String name) {
          this.name = name;
          }

          public int getAge() {
          return age;
          }

          public void setAge(int age) {
          this.age = age;
          }

          public String getMsg() {
          return msg;
          }

          public void setMsg(String msg) {
          this.msg = msg;
          }

          }


          反射相關(guān)操作


          文章開(kāi)始說(shuō)過(guò),反射會(huì)把一個(gè)類(lèi)的成分(成員變量,方法,構(gòu)造器)各自映射成一個(gè)個(gè)對(duì)象(Field對(duì)象,Method對(duì)象,Construct對(duì)象),


          一個(gè)類(lèi)中這些成員方法、構(gòu)造方法、在加入類(lèi)中都有一個(gè)類(lèi)來(lái)描述。


          java.lang.reflect.Constructor; java.lang.reflect.Field;  java.lang.reflect.Method;  java.lang.reflect.Modifier;


          通過(guò)Class對(duì)象我們可以做什么呢?


          • 獲取成員方法Method

          • 獲取成員變量Field

          • 獲取構(gòu)造函數(shù)Construct


          獲取成員方法


          用法


          public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到該類(lèi)所有的方法,不包括父類(lèi)的
          public Method getMethod(String name, Class<?>... parameterTypes) // 得到該類(lèi)所有的public方法,包括父類(lèi)的

          //具體使用
          Method[] methods = class1.getDeclaredMethods();//獲取class對(duì)象的所有聲明方法
          Method[] allMethods = class1.getMethods();//獲取class對(duì)象的所有public方法 包括父類(lèi)的方法
          Method method = class1.getMethod("info", String.class);//返回次Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的public方法
          Method declaredMethod = class1.getDeclaredMethod("info", String.class);//返回次Class對(duì)象對(duì)應(yīng)類(lèi)的、帶指定形參列表的方法


          例子


          public static void main(String[] args) {
          try {
          Class c = Class.forName("General.Student");
          Object o = c.newInstance();
          Method method = c.getMethod("fun",String.class,int.class);
          method.invoke(o,"jieMing",21);
          } catch (Exception e) {
          e.printStackTrace();
          }
          }


          結(jié)果

          只要知道包的限定名,就可以對(duì)Student這個(gè)類(lèi)進(jìn)行所有操作


          獲取成員變量信息


          成員變量 = 成員類(lèi)型+變量名


          用法


          獲取成員變量,通過(guò)Class類(lèi)的以下方法,變量是成員變量名


          public Field getDeclaredField(String name) // 獲得該類(lèi)自身聲明的所有變量,不包括其父類(lèi)的變量
          public Field getField(String name) // 獲得該類(lèi)自所有的public成員變量,包括其父類(lèi)變量

          //具體實(shí)現(xiàn)
          Field[] allFields = class1.getDeclaredFields();//獲取class對(duì)象的所有屬性
          Field[] publicFields = class1.getFields();//獲取class對(duì)象的public屬性
          Field ageField = class1.getDeclaredField("age");//獲取class指定屬性
          Field desField = class1.getField("des");//獲取class指定的public屬性


          例子


          public static void main(String[] args) {
          try {
          Class c = Class.forName("General.Student");
          Object o = c.newInstance();
          Field field = c.getDeclaredField("msg");//msg在例子中是私有變量,如果沒(méi)設(shè)置之前,用c.getField()是會(huì)報(bào)錯(cuò)的
          field.setAccessible(true); //private設(shè)置為public
          System.out.println(c.getField("msg")); //用getFiled()則可以直接訪問(wèn)
          } catch (Exception e) {
          e.printStackTrace();
          }
          }


          獲取構(gòu)造函數(shù)信息


          獲取構(gòu)造函數(shù),Class的方法如下


          用法


          public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //  獲得該類(lèi)所有的構(gòu)造器,不包括其父類(lèi)的構(gòu)造器
          public Constructor<T> getConstructor(Class<?>... parameterTypes) // 獲得該類(lèi)所以public構(gòu)造器,包括父類(lèi)

          //具體
          Constructor<?>[] allConstructors = class1.getDeclaredConstructors();//獲取class對(duì)象的所有聲明構(gòu)造函數(shù)
          Constructor<?>[] publicConstructors = class1.getConstructors();//獲取class對(duì)象public構(gòu)造函數(shù)
          Constructor<?> constructor = class1.getDeclaredConstructor(String.class);//獲取指定聲明構(gòu)造函數(shù)
          Constructor publicConstructor = class1.getConstructor(String.class);//獲取指定聲明的public構(gòu)造函數(shù)


          例子


          Student類(lèi)的私有構(gòu)造函數(shù)


          private Student(String name){
          System.out.println(name);
          }


          獲取私有的構(gòu)造函數(shù),并且設(shè)置為public從而可以創(chuàng)建對(duì)象


          public static void main(String[] args) {
          try {
          Class c = Class.forName("General.Student");
          Constructor constructor = c.getDeclaredConstructor(String.class);
          constructor.setAccessible(true); //如果把這行注釋掉,調(diào)用private的構(gòu)造函數(shù)則會(huì)報(bào)錯(cuò)
          constructor.newInstance("JieMingLi");
          } catch (Exception e) {
          e.printStackTrace();
          }
          }


          結(jié)果

          實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)增,查。


          原理


          保存數(shù)據(jù)時(shí):把pojo類(lèi)的屬性取出來(lái),拼湊sql語(yǔ)句


          查詢(xún)數(shù)據(jù)的時(shí):把查詢(xún)到的數(shù)據(jù)包裝成一個(gè)Java對(duì)象


          一張數(shù)據(jù)表對(duì)應(yīng)java的一個(gè)pojo對(duì)象,表中的每一個(gè)字段(column)對(duì)應(yīng)pojo的每一個(gè)屬性


          數(shù)據(jù)表名和Pojo的類(lèi)名相等,column和pojo的屬性相等,不區(qū)分大小寫(xiě)(數(shù)據(jù)庫(kù)中不區(qū)分大小寫(xiě))


          pojo的每一個(gè)屬性的get和set方法,都是為了后續(xù)的操作


          實(shí)例


          數(shù)據(jù)表User

          pojo User類(lèi)


          package dbtest;

          public class User {
          private int id;
          private String name;
          private String pwd;
          private int age;

          @Override
          public String toString() {
          return "User{" +
          "id=" + id +
          ", name='" + name + '\'' +
          ", pwd='" + pwd + '\'' +
          ", age=" + age +
          '}';
          }

          public int getId() {
          return id;
          }

          public void setId(int id) {
          this.id = id;
          }

          public String getName() {
          return name;
          }

          public void setName(String name) {
          this.name = name;
          }

          public String getPwd() {
          return pwd;
          }

          public void setPwd(String pwd) {
          this.pwd = pwd;
          }

          public int getAge() {
          return age;
          }

          public void setAge(int age) {
          this.age = age;
          }
          }


          數(shù)據(jù)庫(kù)連接的工廠類(lèi)


          package dbtest;

          import java.sql.Connection;
          import java.sql.DriverManager;

          public class ConnectDBFactory {
          public static Connection getDBConnection(){
          Connection conn = null;
          try {
          Class.forName("com.mysql.jdbc.Driver");
          String url = "jdbc:mysql://localhost:3306/sm";
          String user = "root";
          String password = "123456";
          conn = DriverManager.getConnection(url,user,password);
          } catch (Exception e) {
          e.printStackTrace();
          }
          return conn;
          }
          }


          操作數(shù)據(jù)庫(kù)的dao


          package dbtest;

          import org.springframework.web.bind.annotation.ResponseBody;

          import java.lang.reflect.InvocationTargetException;
          import java.lang.reflect.Method;
          import java.sql.*;
          import java.util.ArrayList;
          import java.util.List;

          public class SqlSession {

          public static String getSaveObjectSql(Object o) throws InvocationTargetException, IllegalAccessException {

          String sql = "insert into ";
          /*獲取Class對(duì)象*/
          Class c = o.getClass();
          /*獲取pojo所有的方法*/
          Method[] methods = c.getDeclaredMethods();
          /*獲取全類(lèi)名*/
          String cName = c.getName();
          /*通過(guò)全類(lèi)名獲取數(shù)據(jù)庫(kù)名稱(chēng)*/
          String tableName = cName.substring(cName.lastIndexOf(".")+1,cName.length());
          sql+= tableName + "(";
          /*字段名字*/
          List<String> fieldList = new ArrayList<>();
          /*字段對(duì)應(yīng)的值*/
          List valueList = new ArrayList();

          /*遍歷Class對(duì)象的Method對(duì)象,就可以執(zhí)行相對(duì)于的方法了*/
          for (Method method :
          methods) {
          String methodName = method.getName();
          /*找出get方法,并設(shè)置值*/
          if(methodName.startsWith("get") && !method.equals("getClass")){
          String fieldName = methodName.substring(3,methodName.length());
          fieldList.add(fieldName);
          Object res = method.invoke(o,null);
          if(res instanceof String){
          valueList.add("\""+res+"\"");
          }else{
          valueList.add(res);
          }
          }
          }

          /*拼接sql語(yǔ)句的字段*/
          for (int i = 0; i <fieldList.size() ; i++) {
          if(i < fieldList.size() - 1){
          sql += fieldList.get(i) + ",";
          }else{
          sql += fieldList.get(i) + ") values (";
          }
          }

          /*拼接sql語(yǔ)句的值*/
          for (int i = 0; i <valueList.size() ; i++) {
          if(i < valueList.size()-1){
          sql += valueList.get(i) + ",";
          }else{
          sql += valueList.get(i) + ")";
          }
          }

          return sql;
          }

          /*保存數(shù)據(jù)的操作*/
          public int saveObject(Object o) throws InvocationTargetException, IllegalAccessException, SQLException {
          Connection connection = ConnectDBFactory.getDBConnection();
          String sql = getSaveObjectSql(o);
          PreparedStatement statement = connection.prepareStatement(sql);
          int i = 0;
          i = statement.executeUpdate();
          return i;
          }

          /*
          * 查詢(xún)數(shù)據(jù),查詢(xún)出來(lái)的數(shù)據(jù)映射到pojo的每一個(gè)屬性上
          * */

          public Object getObject(String pname,int id) throws ClassNotFoundException {
          /*通過(guò)包名獲取數(shù)據(jù)表名*/
          String tableName = pname.substring(pname.lastIndexOf(".")+1,pname.length());
          String sql = "select * from " + tableName + " where Id = " + id;
          Connection conn = ConnectDBFactory.getDBConnection();
          Class c = Class.forName(pname);
          Object obj = null;
          try{
          Statement statement = conn.createStatement();
          ResultSet resultSet = statement.executeQuery(sql);
          Method[] methods = c.getDeclaredMethods();

          while(resultSet.next()){
          obj = c.newInstance();
          for (Method method :methods
          ) {
          String methodName = method.getName();
          if(methodName.startsWith("set")){
          /*通過(guò)方法名獲取數(shù)據(jù)庫(kù)的列名*/
          String columnName = methodName.substring(3,methodName.length());
          /*獲取參數(shù)的類(lèi)型*/
          Class[] params = method.getParameterTypes();
          /*判斷參數(shù)的類(lèi)型*/
          if(params[0] == String.class){
          method.invoke(obj,resultSet.getString(columnName));
          }
          if(params[0] == int.class){
          method.invoke(obj,resultSet.getInt(columnName));
          }
          }
          }
          }
          }catch (Exception e){
          e.printStackTrace();
          }
          return obj;
          }

          public static void main(String[] args) {
          try{
          SqlSession session = new SqlSession();
          User user = new User();
          user.setAge(22);
          user.setName("JiemingLi");
          user.setId(44);
          user.setPwd("123456");
          int resNum = session.saveObject(user);
          if(resNum > 0){
          System.out.println("成功");
          }else{
          System.out.println("插入失敗");
          }
          User res = (User)session.getObject("dbtest.User",44);
          System.out.println(res);
          }catch (Exception e){
          e.printStackTrace();
          }
          }
          }


          結(jié)果

          總結(jié)


          Java反射非常好用,靈活性非常大,不用花費(fèi)太多的時(shí)間去寫(xiě)操作數(shù)據(jù)庫(kù)的代碼,讓重點(diǎn)在開(kāi)發(fā)者的業(yè)務(wù)邏輯上。現(xiàn)在很多和數(shù)據(jù)庫(kù)操作的框架都用到反射,只要配置文件,按照框架的規(guī)則就可以對(duì)數(shù)據(jù)庫(kù)進(jìn)行相對(duì)應(yīng)的操作了。


          瀏覽 93
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美色图日韩色图 | 九一福利在线观看 | 日批网站在线播放 | av资源天堂 | 亚洲日本精品一区 |