一、静态语言与动态语言

静态类型语言:是指在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型,某些具有类型推导能力的现代语言可能能够部分减轻这个要求。强类型

动态类型语言:是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。弱类型

静态:C#,Java,C,C++

动态:JavaScript,python,perl,ruby,php

静态类型语言因为类型强制声明,所以IDE可以做到很好的代码感知能力,因为有IDE的撑腰,所以开发大型系统,复杂系统比较有保障。

静态语言相对比较封闭的特点,使得第三方开发包对代码的侵害性可以降到很低。动态语言在这点上表现的就比较差,我想大家都有过从网上下载某个JS包,然后放到项目代码里发生冲突的经历。

 

二、反射概要

反射就是可以在运行时加载类信息,并可以创建其对象访问其属性和方法。这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;生成动态代理。
  • 动态获得类型信息(方法,字段,属性…)
  • 动态时创建对象(根据字符串创建对象)
  • 动态调用对象或成员(动态调用方法)

优点:

可以实现动态创建对象和编译,体现出很大的灵活性

缺点:

对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它 满足我们的要求。这类操作总是慢于只直接执行相同的操作。

 

三、Class

Class 类十分特殊。它和一般类一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。因为Class并没有公共的构造方法,只有一个私有的无参构造方法,也就是说 Class 的对象创建只有 JVM 可以完成。

JAVA 使用 Class 对象来执行运行时类型信息(RunTime Type Information,RTTI)。每个类都有一个 Class 对象,它用来创建这个类的所有对象,反过来说,每个类的所有对象都会关联同一个Class 对象(对于数组来说,维数、类型一致的数组的 Class 对象才是相同的),每个对象的创建都依赖于Class 对象的是否创建,Class 对象的创建发生在类加载(java.lang.ClassLoader)的时候。

理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息。Class对象就是用来创建所有“常规”对象的,Java使用Class对象来执行RTTI,即使你正在执行的是类似类型转换这样的操作。

每个类都会产生一个对应的Class对象,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类。Class对象仅在需要的时候才会加载,static初始化是在类加载时进行的。

简而言之:Class类是类的类

获取Class对象的办法:

package com.ft.suanfa.demo0125;

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Fruit fruit = new Apple();
        System.out.println("今天吃:"+fruit.name );
        //获取Class办法一,通过对象获取
        Class<? extends Fruit> class1 = fruit.getClass();
        //获取Class办法二,通过字符串获得(包名+类名)
        Class<?> orange = Class.forName("com.ft.suanfa.demo0125.Orange");
        //获取Class办法三:通过类的静态成员class获得
        Class clazz3 =Fruit.class;
        //获取Class办法四:(只针对内置的基本数据类型)
        Class<Integer> type = Integer.TYPE;
        
        //获取父类类型
        Class<?> superclass = orange.getSuperclass();
        System.out.println("类名: "+class1.getName());
        System.out.println("类名: "+orange.getName());
        System.out.println("类名: "+clazz3.getName());
        System.out.println("类名: "+type.getName());
        
        System.out.println("com.ft.suanfa.demo0125。Orange的父类类名: "+superclass.getName());
        
        
    }

}

class Fruit{
    public String name;
}

class Apple extends Fruit{
    public Apple() {
        this.name = "苹果";
    }
}

class Orange extends Fruit{
    public Orange() {
        this.name = "橘子";
    }
}

打印输出:

今天吃:苹果
类名: com.ft.suanfa.demo0125.Apple
类名: com.ft.suanfa.demo0125.Orange
类名: com.ft.suanfa.demo0125.Fruit
类名: int
com.ft.suanfa.demo0125。Orange的父类类名: com.ft.suanfa.demo0125.Fruit

 

 

四、反射的使用

1、获得方法信息

演示代码:

package com.ft.suanfa.demo0125;

/**
 * 计算器
 * @author zhang-shenyong
 *
 */
public class Calculator {
    public String name;
    public int a;
    public int b;
    
    //
    public void add() {
        System.out.println(a+"+"+b+"="+(a+b));
    }
    //
    public void sub() {
        System.out.println(a+"-"+b+"="+(a-b));
    }
    //
    public void mut() {
        System.out.println(a+"*"+b+"="+(a*b));
    }
    //
    public void div() {
        System.out.println(a+"/"+b+"="+(a/b));
    }
}

package com.ft.suanfa.demo0125;

import java.lang.reflect.Method;

public class RefTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取Calculator的Class对象
        Class<?> clazz1 = Class.forName("com.ft.suanfa.demo0125.Calculator");
        
        //获取所有方法
        Method[] methods = clazz1.getMethods();
        for (Method m : methods) {
            System.out.println(m.getName()+",,"+m.getParameterCount());
        }
    }

}

打印输出:
add,,0
sub,,0
div,,0
mut,,0
wait,,0
wait,,2
wait,,1
equals,,1
toString,,0
hashCode,,0
getClass,,0
notify,,0
notifyAll,,0

 

上诉前4个方法是我们自己实现的,后面的方法是继承Object类的方法

2、实例化对象

package com.ft.suanfa.demo0125;

import java.lang.reflect.Method;

public class RefTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //获取Calculator的Class对象
        Class<?> clazz1 = Class.forName("com.ft.suanfa.demo0125.Calculator");
        
        //获取所有方法
        Method[] methods = clazz1.getMethods();
        for (Method m : methods) {
            System.out.println(m.getName()+",,"+m.getParameterCount());
        }
        
        //实例化对象
        //根据Class对象创建的实例,调用无参构造方法,注意,必须要有无参构造
        Calculator cal =(Calculator)clazz1.newInstance();
        //调用方法;
        cal.add();
    }

}

打印输出:
0+0=0

 

3、获取成员变量并赋值,调用方法

package com.ft.suanfa.demo0125;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class RefTest {
    public static void main(String[] args) throws Exception {
        //获取Calculator的Class对象
        Class<?> clazz1 = Class.forName("com.ft.suanfa.demo0125.Calculator");
        
        //获取所有方法
//        Method[] methods = clazz1.getMethods();
//        for (Method m : methods) {
//            System.out.println(m.getName()+",,"+m.getParameterCount());
//        }
        
        //实例化对象
        //根据Class对象创建的实例,调用无参构造方法,注意,必须要有无参构造
        Calculator cal =(Calculator)clazz1.newInstance();
//        //调用方法;
//        cal.add();
//        
        //获取成员变量a,b,name
        //Field[] fields = clazz1.getFields();
        Field a = clazz1.getField("a");
        Field b = clazz1.getField("b");
        a.set(cal, 99);
        b.set(cal, 98);
        //动态调用方法
        Method m1 = clazz1.getMethod("add");
        m1.invoke(cal);
        Method m2 = clazz1.getMethod("sub");
        m2.invoke(cal);
        
    }

}

打印输出:
99+98=197
99-98=1

 

五、类的生命周期

在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。

类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载以前

链接就是把二进制数据组装为可以运行的状态。

 

链接分为校验,准备,解析这3个阶段

校验一般用来确认此二进制文件是否适合当前的JVM(版本),

准备就是为静态成员分配内存空间,。并设置默认值

解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)

完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。

当没有任何引用指向Class对象时就会被卸载,结束类的生命周期

将反射用于工厂模式

package com.ft.suanfa.demo0126;

public interface Fruit {
    public void eat();
}

class Apple implements Fruit{

    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("Apple");
    }
    
}

class Orange implements Fruit{

    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("Orange");
    }
    
}

//构造工厂类
//也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了
class Factory{
    public static Fruit getInstance(String fruitName) {
        Fruit f = null;
        if("Apple".equals(fruitName)) {
            f = new Apple();
        }
        if("Orange".equals(fruitName)) {
            f = new Orange();
        }
        return f;
    }
}

class MyTest{
    public static void main(String[] args) {
        Fruit instance = Factory.getInstance("Orange");
        instance.eat();
    }
}

 

这样,当我们在添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。

现在我们看看利用反射机制:

package com.ft.suanfa.demo0126;

interface Fruit {
    public void eat();
}

class Apple implements Fruit{

    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("Apple");
    }
    
}

class Orange implements Fruit{

    public void eat() {
        // TODO Auto-generated method stub
        System.out.println("Orange");
    }
    
}

//构造工厂类
//也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了
class Factory{
    public static Fruit getInstance(String fruitName) {
        //使用传统方式
//        Fruit f = null;
//        if("Apple".equals(fruitName)) {
//            f = new Apple();
//        }
//        if("Orange".equals(fruitName)) {
//            f = new Orange();
//        }
//        return f;
        //使用反射方式
        Fruit f = null;
        try {
            f = (Fruit) Class.forName(fruitName).newInstance();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return f;
    }
}

 class MyTest{
    public static void main(String[] args) {
        //Fruit instance = Factory.getInstance("Orange");
        Fruit instance = Factory.getInstance("com.ft.suanfa.demo0126.Apple");//传入类的全类名
        if(instance != null) {
            instance.eat();
        }
        
    }
}

现在就算我们添加任意多个子类的时候,工厂类就不需要修改。

 

六、注解

1、注解的定义

Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。


Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

2、

 

posted on 2021-01-25 12:55  二十二画生的执著  阅读(84)  评论(0编辑  收藏  举报