01-JAVA编程基础

基础知识

计算机中,二进制都是补码的形式表示的,补码的首位是符号位。

为什么在计算机中使用补码?
正数在计算机中表示形式与真实世界的二进制表示形式一致;
正数不存在什么补码。采用补码就是用来表示负数的。
例如:
1的原码: [0000 0000 0000 0000 0000 0000 0000 0001]
1的反码:[1111 1111 1111 1111 1111 1111 1111 1110]
则-1在计算机中就用1的补码表示:
1的补码: [1111 1111 1111 1111 1111 1111 1111 1111]

那么0没有正数和负数之分,怎么办。
正数0:[0000 0000 0000 0000 0000 0000 0000 0000]
负数0:按照补码的作用来看,可以对正数0进行补码操作后得到负数0:
[1000 0000 0000 0000 0000 0000 0000 0000]
0不区分正负数,却在计算机中占用两个编码?
这样吧,规定其中的[1000 0000 0000 0000 0000 0000 0000 0000] = -2147483648 ;
不仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数。

要知道int的最大正数 [0111 1111 1111 1111 1111 1111 1111 1111]的十进制是2147483647。 按理说最小值应该为-2147483647(即正数的补码): [1000 0000 0000 0000 0000 0000 0000 0001];但是0不能占两种码,其中被“嫌弃”的码给了最小的负数,在int中就是-2147483648;所以int取值范围:-2147483648 ~2147483647。

public class App {
    public static void main(String[] args) {

        // 左移 左移后,低位补0
        //无符号左移<<<和左移<<是一样的概念
        // 5的二级制:0000 0000 0000 0000 0000 0000 0000 0101
        System.out.println(5<<5);  // 160=5*2*2*2*2*2

        //  >>>   无符号右移   低位溢出,高位补0  
        //无符号的意思是将符号位当作数字位看待
        // 5的二级制:0000 0000 0000 0000 0000 0000 0000 0101
        //  >>>       0000 0000 0000 0000 0000 0000 0000 0101
        //          0000 0000 0000 0000 0000 0000 0000 0001
        System.out.println(5>>>2); //1

        //位与&
        //    0000 0000 0000 0000 0000 0000 0000 0101     5
        // &  0000 0000 0000 0000 0000 0000 0000 0011     3
        //    0000 0000 0000 0000 0000 0000 0000 0001     1
        System.out.println(5&3); //1

        //位异或 ^
        //    0000 0000 0000 0000 0000 0000 0000 0101     5
        // ^  0000 0000 0000 0000 0000 0000 0000 0011     3
        //    0000 0000 0000 0000 0000 0000 0000 0110     6

       System.out.println(5^3); // 6

        // 位非  ~  一元操作符   按位取反
        //     0000 0000 0000 0000 0000 0000 0000 0101     5
        //  ~  1111 1111 1111 1111 1111 1111 1111 1010    -6
        // 原理:11111111111111111111111111111111  = -1
        System.out.println(~5);// -6
    }
    }

其中整数的数据类型除了符号位,剩下的都是数值位;而浮点数值除了符号位,剩下的位数由指数位+尾数位组成。这就解释以下问题:

a占32位;b占64位;a == b; 两者32中的数值完全一致,故相等。
c占32位;d占64位;c == d; 两者符号位相等,指数位相等,但尾数位不相等,故不相等。

关于编码的事情:
由于计算机最先由美国发明,为了将英文和符号存入计算机,必须先将英文和符号用二进制做编码和对照。这个对照️表:ASCII。
后来发展到中国有了GBK。
为了统一全世界编码,最后unicode制定了unicode 规范,用三个字节表示!其中第一个字节表示:后面两个字节表示的是一个字符还是两个字符。
美国、西欧和中国不干了,美国和西欧的字符只需要一个字节就够,凭什么用三个字节,中国也不干,中国的字符用两个字节就够了!

后来在unicode规范下,衍生出了UTF-8/16/32编码方式,其特点就是根据编码分类,根据不同的组,其字符占用不同的字节!

char 类型:

  • 其默认值:16进制的'\u0000'。
  • 可以运算:按照UTF-16编码表对应的数值进行运算
  • 数字取值范围是0~65535。
public class PrimitiveType {
    char c; // 使用默认值
    @Test
    public void testChar() {
        PrimitiveType t = new PrimitiveType();
        char c1 = '\u0000'; // 指定默认值
        char c2 = '\0';
        char c3 = '\u2400'; // 对应字符 NUL
        char c4 = '\u0020'; // 对应字符 空格
        System.out.println("c1: " + c1); //c1:
        System.out.println("c2: " + c2);//c2:
        System.out.println("c3: " + c3);//c3: NUL
        System.out.println("c4: " + c4);//c4:
        System.out.println("c: " + c);//c:
        System.out.println("c1==c: " + (c1 == t.c));//c1==c: true
        System.out.println("c1==c2: " + (c1 == c2));//c1==c2: true
        System.out.println("c1==c3: " + (c1 == c3));//c1==c3: false
        System.out.println("c1==c4: " + (c1 == c4));//c1==c4: false
    }
}

运算:

char m = 'a';       // 输出 a
char m = 'a' + 'b'; // 提升为 int 类型相加 97 + 98, 然后输出对应的字符.
int m = 'a' + 'b'; // 输出 195.
char m = 'a' + b;   // 报错, 因为b是一个赋值的变量
char m = 195;     // 输出字符编码表中对应的字符
char m = '195';   // 报错, 因为有单引号, 表示字符, 只允许放单个字符
char m = 'a' + 1;  // 输出 b, 提升为 int 类型相加, 计算结果 98 对应的字符是b
char m ='中'+'国'+'国'+'国'; // 报错. 计算结果 86820 超出范围 65535
int m ='中'+'国'+'国'+'国'; // 输出 86820
System.out.println('中'+"国"); // 输出中国.
char m ='中'+"国"; // 报错. String 无法转换为 char.
String str = "我";
byte[] c1 = str.getBytes();
System.out.println(c1.length);
//结果:可能是 2、3、4 之一。
//因为getBytes() 方法会根据当前默认的字符编码格式获取字节数组, 
//gbk/gb2312 占 2 位, utf-8 占 3 位, utf-16 占 4 位, unicode 占 4 位。 
//通过 str.getBytes("GBK") 或 str.getBytes("UTF-8") 来指定编码方式。

一、面向对象特性

1.封装

将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。

1.1 访问修饰符

  • default : 在同一包内可见,无修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。
  • public : 对所有类可见。使用对象:类、接口、变量、方法。
  • protected : 同一包内的类和所有子类可见。使用对象:变量、方法。

protected详解

F和M不是一个包

F和M是一个包

People.java

package  com.xzb.javase.enclosure;
public class People {
    protected void say(){
        System.out.println("protected……People");
    }
}

Human.java

package  com.xzb.javase.enclosure;
public class Human {
   public  void  sayHuman(){
    People people = new People();
    people.say();
}
}

Client.java

package com.xzb.javase.enclosure2;
import com.xzb.javase.enclosure.People;
public class Client {
    public void cloneTest(){
        //protected : 同一包内的类和所有子类可见。使用对象:变量、方法。
        //虽然People是Object的子类,但是Client作用范围内不能使用People爹的clone方法;只能用自己爹的clone方法。
        // 虽然是一个爹。但是!我的地盘听我的。
        People people = new People();
       // people.clone(); //编译失败
        try {
            clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        People people = new People();
        //people.say();//编译失败
    }
}

default详解

package com.xzb.javase.enclosure2;
import com.xzb.javase.enclosure.People;
public class Client extends People{
    public static void main(String[] args) {
        People people = new People();
        //people.say();//编译失败
    }
}

1.2 this关键字

  • this关键字代表当前对象。
  • this.属性 操作当前对象的属性。
  • this.方法 调用当前对象的方法。
  • this() 构造方法之间的互相调用。

1.3 内部类

  • Java内部类的分类:成员内部类、局部内部类、匿名内部类和静态内部类。
  • 内部类的使用场景
    1、几个类的逻辑关系很强,同时想对外隐藏这些类;
    2、线程类中;
    3、类中要实现多继承;
  • 内部类作用
    Java不支持多继承,内部类可以完善Java的多继承机制。
public class OuterClass {
    private String str;
    //1.成员内部类:作为外部类的成员属性使用
    public class MemberInnerClass {
          public void display() {
              str = "MemberInnerClass";
              System.out.println(str);//可以访问外部类成员
        }
    }
    //2.局部内部类:内部类作用仅限于作用域内
    public void display(boolean b) {
        //2.1 定义在方法内
        class MethodInnerClass {
            public void display() {
                System.out.println("MethodInnerClass");
            }
        }
        new MethodInnerClass().display();
        //2.2 定义在作用域内
        if (b) {
            class RegionInnerClass {
                public void display() {
                    System.out.println("RegionInnerClass");
                }
            }
            new RegionInnerClass().display();
        }

    }
    //3.静态内部类:创建是不需要依赖于外部类,不能使用任何外部类的非static成员变量和方法
    static class StaticInnerClass {
        public void display() {
            System.out.println("StaticInnerClass");
        }
    }
    //4.匿名内部类:
    public void display() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("AnonymInnerClass");
            }
        }).start();
    }

    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.new MemberInnerClass().display();
        outerClass.display(true);
        new StaticInnerClass().display();
        outerClass.display();
    }
}

2.继承

2.1 继承的初始化顺序

public class Test {
    public static void main(String[] args) {
        Child c=new Child();
    }
}
class Parent {
    public static PrintMessage a=new PrintMessage("父类静态成员被初始化");
    private PrintMessage b=new PrintMessage("父类非静态成员被初始化"); 
    static{
        System.out.println("父类的静态代码块被执行");
    }
    {
        System.out.println("父类的非静态代码块被执行");
    }
    public Parent(){
        System.out.println("父类的构造方法被执行");
    }   
}

class Child extends Parent{
    public static PrintMessage a1=new PrintMessage("子类静态成员被初始化");
    private PrintMessage b1=new PrintMessage("子类非静态成员被初始化");    
    
    static {
        System.out.println("子类的静态代码块被执行");
    }
    {
        System.out.println("子类的非静态代码块被执行");
    }
    public Child(){
        System.out.println("子类的构造函数被执行");
    }
}
class PrintMessage{
    public PrintMessage(String mes){
        System.out.println(mes);
    }
}
执行结果

2.2 final关键字

  • final 修饰类,则该类不允许被继承。
  • final 修饰方法,则该方法不允许被重写。
  • final 修饰属性,则该类的该属性不会进行隐式的初始化,所以 该final 属性的初始化属性必须有值,或在构造方法中赋值(但只能选其一,且必须选其一,因为没有默认值!),且初始化之后就不能改了,只能赋值一次。
  • final 修饰变量,则该变量的值只能赋一次值,在声明变量的时候才能赋值,即变为常量。String的变量就是被final修饰,这样导致变量变成常量,被放在常量池中。

2.3 super关键字

在对象的内部使用,可以代表父类对象。

  • 访问父类的属性:super.age
  • 访问父类的方法:super.eat()

super的应用:
子类的构造过程中必须调用父类的构造方法。这个过程我们使用super关键字隐式调用。如果自己用super关键字在子类里调用父类的构造方法,则必须在子类的构造方法中的第一行。
1.子类构造方法没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身其他的构造方法,系统默认调用父类的无参构造方法。Super()。
2.子类构造方法通过super显式调用父类的有参构造方法,执行父类相应构造方法,而不执行父类无参构造方法。

3.多态

3.1 引用多态

父类的引用可以指向本类的对象,可以指向子类的对象;

3.2 方法多态

本类对象和子类对象,同样都是父类的引用,当我们指向不同的对象时,它们调用的方法也是多态的。

  • 本类对象时,调用的方法为本类方法;
  • 创建子类对象时,调用的方法为子类重写的方法或者继承的方法;

3.3 抽象类

抽象类前使用abstract关键字修饰,则该类为抽象类。

  1. 抽象类不能被实例化。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

3.4 接口

  1. 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  2. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

抽象类是定义模版。接口类是定义规范。

二、Lambda表达式

Lambda是为了简化匿名内部接口。

使用Lambda与匿名内部类的区别

  • 所需类型不一样
    匿名内部类所需的是类,可以是抽象类也可以是接口。Lambda所需的必须是接口。
  • 实现原理不同
    匿名内部类是在编译后生成一个class文件。
    Lambda是在运行时动态生成的class文件,且会生成一个新的方法。
原理区别

三、方法引用

函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟。

3.1 应用场景

来看一个简单的函数式接口以应用Lambda表达式:

public class Demo {
    private static void printString(String str,Consumer<String> sss) {
        sss.accept(str);
    }

    public static void main(String[] args) {

        printString("xzb", new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);//整个逻辑已经有实现了。
            }
        });
        //lamdba
        printString("xzb1",(s)-> System.out.println(s));
        //方法引用
        printString("xzb3",System.out::println);
    }
}

双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

3.2 对象名引用成员方法

定义一个打印的函数式接口:

@FunctionalInterface
public interface Printable {
    void print(String s);
}
package com.xzb.javase.lambda;

public class DemoMethodReference {

    public static void printString(Printable p){
        p.print("Hello");
    }
    public static void main(String[] args) {

        // 使用匿名内部类
        printString(new Printable() {
            @Override
            public void print(String s) {
                System.out.println(s.toUpperCase());
            }
        });

        //使用lamdba
        printString((s)->{
            System.out.println(s.toUpperCase());
        });

        //使用方法引用
        MethodRerObject obj = new MethodRerObject();
        printString(obj::printUpperCaseString);

    }

    static class MethodRerObject {
        public void printUpperCaseString(String str){
            System.out.println(str.toUpperCase());
        }
    }

}

3.3 静态方法引用

@FunctionalInterface
public interface Calcable {
    int calsAbs(int number);
}
/*
    通过类名引用静态成员方法
    类已经存在,静态成员方法也已经存在
    就可以通过类名直接引用静态成员方法
 */
public class Demo01StaticMethodReference {
    public static int method(int number,Calcable c){
       return c.calsAbs(number);
    }

    public static void main(String[] args) {
         //lamdba
        int number = method(-10,(n)->{
          return Math.abs(n);  //对参数进行绝对值得计算并返回结果;该逻辑已经有实现类:Math
        })
        //方法引用:
        int number2 = method(-10,Math::abs);
    }
}

3.4 super引用父类成员方法

@FunctionalInterface
public interface Greetable {
    //定义一个见面的方法
    void greet();
}
public class Human {
    public void sayHello(){
        System.out.println("Hello 我是Human!");
    }
}

public class Man extends Human{
      //定义一个方法参数传递Greetable接口
    public void method(Greetable g){
        g.greet();
    }

    public void show(){
        //调用method方法,方法的参数Greetable是一个函数式接口,所以可以传递Lambda
        //因为有子父类关系,所以存在的一个关键字super,代表父类,所以我们可以直接使用super调用父类的成员方法
       /* method(()->{
            super.sayHello();
        });*/
      method(super::sayHello);
    }

    public static void main(String[] args) {
        new Man().show();
    }
}

3.5 this引用本类方法

@FunctionalInterface
public interface Richable {
    void buy();
}

public class Husband {
    //定义一个买房子的方法
    public void buyHouse(){
        System.out.println("北京二环内买一套四合院!");
    }

    //定义一个结婚的方法,参数传递Richable接口
    public void marry(Richable r){
        r.buy();
    }

    //定义一个非常高兴的方法
    public void soHappy(){
        //调用结婚的方法,方法的参数Richable是一个函数式接口,传递Lambda表达式
       /* marry(()->{
            //使用this.成员方法,调用本类买房子的方法
            this.buyHouse();
        });*/
        marry(this::buyHouse);
    }

    public static void main(String[] args) {
        new Husband().soHappy();
    }
}

3.6 类构造函数的引用

public class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

@FunctionalInterface
public interface PersonBuilder {
    //定义一个方法,根据传递的姓名,创建Person对象返回
    Person builderPerson(String name);
}
public class Demo {
    //定义一个方法,参数传递姓名和PersonBuilder接口,方法中通过姓名创建Person对象
    public static void printName(String name,PersonBuilder pb){
        Person person = pb.builderPerson(name);
        System.out.println(person.getName());
    }
    //lamdba
    public static void main(String[] args) {
        printName("迪丽热巴",(String name)->{
            return new Person(name);
        });
    //方法引用
    printName("古力娜扎",Person::new);//使用Person类的带参构造方法,通过传递的姓名创建对象
    }
}

3.7 数组构造器的引用

@FunctionalInterface
public interface ArrayBuilder {
    //定义一个创建int类型数组的方法,参数传递数组的长度,返回创建好的int类型数组
    int[] builderArray(int length);
}
public class Demo {
    public static int[] createArray(int length, ArrayBuilder ab){
        return  ab.builderArray(length);
    }

    public static void main(String[] args) {
        //调用createArray方法,传递数组的长度和Lambda表达式
        int[] arr1 = createArray(10,(len)->{
            return new int[len];
        });
        System.out.println(arr1.length);//10

       //方法引用
        int[] arr2 =createArray(10,int[]::new);
        System.out.println(arr2.length);//10
    }
}

四、反射

将类的各个组成部分封装为其他对象,这就是反射机制。
好处:可以在程序运行过程中,操作这些对象;可以解耦,提高程序的可扩展性。

内省和反射浅谈
反射可以操作各种类的属性,而内省只是通过反射来操作JavaBean的属性;
内省设置属性值肯定会调用seter方法,反射可以不用;
内省一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。

获取Class对象的方式:

  • Class.forName("全类名"):将字节码文件加载进内存,返回Class对象;多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
  • 类名.class:通过类名的属性class获取;多用于参数的传递。
  • 对象.getClass():getClass()方法在Object类中定义着;多用于对象的获取字节码的方式。

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

Class对象功能

         * 获取功能:
         1. 获取成员变量们
             * Field[] getFields()
             * Field getField(String name)

             * Field[] getDeclaredFields()
             * Field getDeclaredField(String name)
         2. 获取构造方法们
             * Constructor<?>[] getConstructors()
             * Constructor<T> getConstructor(类<?>... parameterTypes)

             * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
             * Constructor<?>[] getDeclaredConstructors()
         3. 获取成员方法们:
             * Method[] getMethods()
             * Method getMethod(String name, 类<?>... parameterTypes)

             * Method[] getDeclaredMethods()
             * Method getDeclaredMethod(String name, 类<?>... parameterTypes)

         4. 获取类名
             * String getName()

Person:

public class Person {
    private String name;
    private int age;
    public String a;
    protected String b;
    String c;
    private String d;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    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;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }
    public void eat(){
        System.out.println("eat...");
    }
    public void eat(String food){
        System.out.println("eat..."+food);
    }
}

1.获取成员变量

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        //1.Field[] getFields()获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("------------");
        //2.Field getField(String name)
        Field a = personClass.getField("a");
        //获取成员变量a 的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);

        System.out.println("===================");
        //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        //Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);
    }
}

2.获取构造方法

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
   
        //Constructor<T> getConstructor(类<?>... parameterTypes)
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);

        System.out.println("----------");

        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
        //创建对象  空参构造方法使用Class.newInstance()
        Object person1 = constructor1.newInstance();
        System.out.println(person1);
        Object o = personClass.newInstance();
        System.out.println(o);
        //constructor1.setAccessible(true);
    }
}

3. 获取成员方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {

        //0.获取Person的Class对象
        Class personClass = Person.class;
        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);
        Method eat_method2 = personClass.getMethod("eat", String.class);
        //执行方法
        eat_method2.invoke(p,"饭");
        System.out.println("-----------------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            String name = method.getName();
            System.out.println(name);
            //method.setAccessible(true);
        }
        //获取类名
        String className = personClass.getName();
        System.out.println(className);//cn.itcast.domain.Person
    }
}

应用:模拟Spring
User :

public class User  {
    private  String name;
    private  String nick;
    private  int age;
    public User(){
        System.out.println("我是无参构造方法");
    }
    public User(int age){
         this.age=age;
         System.out.println("我是有参构造方法,初始年龄:"+age);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getNick() {
        return nick;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }
    public   void myFunction(){
        System.out.println("我是自定义方法");
    }
    public   void  pay(){
        System.out.println("我是pay()");
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", nick='" + nick + '\'' +
                ", age=" + age +
                '}';
    }
}

Springs :
假设有个配置文件

<bean id="user" class="com.User">
    <constructor-arg type="java.lang.Integer" value="30" />
     <property name="name" value="xzb" />
     <property name="nick" value="abo" />
     <property name="age" value="20" />
</bean>

假设已经读取配置文件:

import com.User;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class Springs {
    public static void main(String[] args) throws  Exception {
        // IOC 和 DI 只是操作对象属性。
        //模拟IOC
        //1.读取 class="com.User"   反射得到对象   
        Class clazz = Class.forName("com.User");
        Object obj = clazz.newInstance();
        System.out.println(obj);
        //2.1 读取 <constructor-arg type="java.lang.Integer" value="30" />
       //  对象属性注入 使用构造方法    模拟DI  
        Constructor con = clazz.getConstructor(int.class);
        Object objCon = con.newInstance(30);
        System.out.println(objCon);
        //2.2  读取  <property />中的值
        //无参对象属性注入 使用set方法   模拟DI
        System.out.println("----------");
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);//暴力反射
            if ("name".equals(field.getName())) {
              //field.set(obj, "xzb"); 可以直接注入,为了模拟使用set方式
                Method m = clazz.getDeclaredMethod("setName", String.class);
                m.invoke(obj,"xzb");
            }
            if ("nick".equals(field.getName())) {
                field.set(obj,"abo");
            }
            if ("age".equals(field.getName())) {
                field.set(obj,20);
            }
        }
        System.out.println(obj);
        //3.放入容器
        Map beans = new HashMap<String, Object>();
        beans.put(clazz.getName(),obj);
        //5.从容器取对象
        User bean = (User) beans.get("com.User");
        System.out.println(bean);

        //AOP操作对象的方法
        //模拟AOP
        //CGLIB生成代理对象
        final User user = bean;
        User userProxy = (User) Enhancer.create(clazz,new MethodInterceptor(){
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
               if("pay".equals(method.getName())){
                   System.out.println("BEFORE");
                   method.invoke(user, args);
                   System.out.println("AFTER");
               }else{
                   method.invoke(user, args);
               }
                return null;
            }
        });
        //代理对象放入容器
        beans.put("proxy",userProxy);
        //6.从容器取对象
        User beanProxy = (User)beans.get("proxy");
        beanProxy.pay();
        beanProxy.myFunction();
        System.out.println(beanProxy.getClass());
    }
}
结果

五、注解

注解概念:说明程序的,给计算机看的。

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
注解作用分类
①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】

5.1 JDK内置注解

  • @Override :检测被该注解标注的方法是否是继承自父类(接口)的。
  • @Deprecated:该注解标注的内容,表示已过时。
  • @SuppressWarnings:压制警告,一般传递参数all @SuppressWarnings("all")。

5.2 自定义注解

本质:注解本质上就是一个接口,该接口默认继承Annotation接口,自定义注解本质是一个接口类。它的实现类在jvm运行时会自动帮我们创建它的实现类。

public interface MyAnno extends java.lang.annotation.Annotation {}

格式:

元注解
public @interface 注解名称{
属性列表;
}

属性:接口中的抽象方法。

  1. 属性的返回值类型有下列取值: 基本类型、String、枚举、注解、以上类型的数组。
  2. 定义了属性,在使用时需要给属性赋值
  • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
  • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
  • 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略。

元注解:用于描述注解的注解

  1. @Target:描述注解能够作用的位置, ElementType取值: TYPE:可以作用于类上;METHOD:可以作用于方法上; FIELD:可以作用于成员变量上。
  2. @Retention:描述注解被保留的阶段, @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。
  3. @Documented:描述注解是否被抽取到api文档中。
  4. @Inherited:描述注解是否被子类继承。

5.2 注解使用 模拟注解版Spring

定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Service {
    String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Resource {
}

Spring待管理的User:

@Service(value = "user888")
public class User {
    @Resource
    private  String name;
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

使用注解

public class SpringAnoCheck {
    public static void main(String[] args)  throws  Exception {
        User user = new User();
        Map beans = new HashMap<String, Object>();
        // 1.获得字节码文件
        Class cls = user.getClass();
        // 2.检查User是否有@Service注解,有:放入IOC
        if(cls.isAnnotationPresent(Service.class)){
            Service service = (Service)cls.getAnnotation(Service.class);
            beans.put(service.value(),user);
        }
        //3.检查属性是否有@Resource注解,有初始化
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);//暴力反射
            if (field.isAnnotationPresent(Resource.class)) {
                field.set(user, "xzb");
            }
        }
        //4.从容器取对象
        User bean = (User) beans.get("user888");
        System.out.println(bean);
    }
}

结果

总结:注解可替代配置文件,该“配置文件(注解)”的信息在Class文件中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容