知识屋:更实用的电脑技术知识网站
所在位置:首页 > 教育

01-JAVA编程基础[1]

发表时间:2022-03-25来源:网络

基础知识

真实世界,二进制没有符号位。

+[0000 0000 0000 0000 0000 0000 1111 1111] = 255
+[0000 0000 0000 0000 0000 0000 0000 0000] = 0
-[0000 0000 0000 0000 0000 0000 0000 0001] = -1

计算机的世界中,二进制都是补码。第一位是符号位。
最小存储单位为比特(bit),计量基本单位是字节(byte)。1byte=8bit 。

[0000 0000 0000 0000 0000 0000 1111 1111] = 255
[0000 0000 0000 0000 0000 0000 0000 0000] = 0

-1的原码:
[1000 0000 0000 0000 0000 0000 0000 0001]
-1的反码:
[1111 1111 1111 1111 1111 1111 1111 1110]
-1的补码:
[1111 1111 1111 1111 1111 1111 1111 1111]

0的原码: [1000 0000 0000 0000 0000 0000 0000 0000] 和 [0000 0000 0000 0000 0000 0000 0000 0000]
0的补码: [1000 0000 0000 0000 0000 0000 0000 0000] 和 [0000 0000 0000 0000 0000 0000 0000 0000]
0在计算机中占用两个编码?
这样吧,规定其中的[1000 0000 0000 0000 0000 0000 0000 0000] = -2147483648 ;
不仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数。

image.png

public class App { public static void main(String[] args) { // 左移 左移后,低位补0 //无符号左移>>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 } }

一、面向对象特性

1.封装

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

1.1 访问修饰符

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

protected详解
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关键字修饰,则该类为抽象类。

抽象类不能被实例化。只有抽象类的非抽象子类可以创建对象。

抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

抽象类中的抽象方法只是声明,不包含方法体。

构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。

抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。

3.4 接口

1.接口特性

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

2.接口与类的区别

接口不能用于实例化对象。 接口没有构造方法。 接口中所有的方法必须是抽象方法。 接口不能包含成员变量,除了 static 和 final 变量。 接口不是被类继承了,而是要被类实现。 接口支持多继承。

3.接口与抽象类区别
DK 1.8 以后,接口里可以有静态方法和方法体了。

抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

二、Lambda表达式

有且只有一个抽象方法的接口,成为“函数式接口”。

1.使用Lambda必须具有接口,有且接口中仅有一个抽象方法。 2.使用Lambda必须具有上下文推断,即方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。
实例1: package com.itheima.demo03.Lambda; /* Lambda表达式的标准格式: 由三部分组成: a.一些参数 b.一个箭头 c.一段代码 格式: (参数列表) -> {一些重写方法的代码}; 解释说明格式: ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔 ->:传递的意思,把参数传递给方法体{} {}:重写接口的抽象方法的方法体 */ public class Demo02Lambda { public static void main(String[] args) { //使用匿名内部类的方式,实现多线程 new Thread(new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+" 新线程创建了"); } }).start(); //使用Lambda表达式,实现多线程 new Thread(()->{ System.out.println(Thread.currentThread().getName()+" 新线程创建了"); } ).start(); //优化省略Lambda new Thread(()->System.out.println(Thread.currentThread().getName()+" 新线程创建了")).start(); } }

实例2:

package com.itheima.demo05.Lambda; import java.util.Arrays; /* Lambda表达式有参数有返回值的练习 需求: 使用数组存储多个Person对象 对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序 */ public class Demo01Arrays { public static void main(String[] args) { //使用数组存储多个Person对象 Person[] arr = { new Person("柳岩",38), new Person("迪丽热巴",18), new Person("古力娜扎",19) }; //对数组中的Person对象使用Arrays的sort方法通过年龄进行升序(前边-后边)排序 /*Arrays.sort(arr, new Comparator() { @Override public int compare(Person o1, Person o2) { return o1.getAge()-o2.getAge(); } });*/ //使用Lambda表达式,简化匿名内部类 Arrays.sort(arr,(Person o1, Person o2)->{ return o1.getAge()-o2.getAge(); }); //优化省略Lambda Arrays.sort(arr,(o1, o2)->o1.getAge()-o2.getAge()); //遍历数组 for (Person p : arr) { System.out.println(p); } } }

实例3:

package com.itheima.demo07.Lambda; import java.util.ArrayList; /* Lambda表达式:是可推导,可以省略 凡是根据上下文推导出来的内容,都可以省略书写 可以省略的内容: 1.(参数列表):括号中参数列表的数据类型,可以省略不写 2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略 3.{一些代码}:如果{}中的代码只有一行,无论是否有返回值,都可以省略({},return,分号) 注意:要省略{},return,分号必须一起省略 */ public class Demo01ArrayList { public static void main(String[] args) { //JDK1.7版本之前,创建集合对象必须把前后的泛型都写上 ArrayList list01 = new ArrayList(); //JDK1.7版本之后,=号后边的泛型可以省略,后边的泛型可以根据前边的泛型推导出来 ArrayList list02 = new ArrayList(); } }

三、方法引用

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

3.1 应用场景

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

public class Demo { private static void printString(String str,Consumer sss) { sss.accept(str); } public static void main(String[] args) { printString("xzb", new Consumer() { @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方法,然后我们就可以通过反射机制来调用这些方法。

image.png

获取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 getConstructor(类... parameterTypes) * Constructor 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 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 :
假设有个配置文件

假设已经读取配置文件:

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 读取 // 对象属性注入 使用构造方法 模拟DI Constructor con = clazz.getConstructor(int.class); Object objCon = con.newInstance(30); System.out.println(objCon); //2.2 读取 中的值 //无参对象属性注入 使用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(); 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 注解名称{ 属性列表; }

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

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

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

@Target:描述注解能够作用的位置, ElementType取值: TYPE:可以作用于类上;METHOD:可以作用于方法上; FIELD:可以作用于成员变量上。 @Retention:描述注解被保留的阶段, @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。 @Documented:描述注解是否被抽取到api文档中。 @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(); // 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文件中。

收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜