@注释名
在代码中存在的,还可以添加一些参数值@SuppressWarnings(value = "unchecked")
java.lang.Override
中,此注释只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明java.lang.Deprecated
中,此注释可以用于修饰方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为它很危险,或者存在更好的选择java.lang.SuppressWarnings
中,用来抑制编译时的警告信息,与前面的两个注释不同,你需要额外添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了。
元注解的作用就是负责注解其它注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其它annotation类型作说明。
这些类型和它们所支持的类在 java.lang.annotation
包可以找到 @Target
、@Retention
、@Documented
、@Inherited
示例
/**
* 元注解
*
* @author: 陌溪
* @create: 2020-03-28-22:57
*/
@MyAnnotation
public class MateAnnotationDemo {
}
/**
* 定义一个注解
*/
@Target(value={ElementType.METHOD, ElementType.TYPE}) // target表示我们注解应用的范围,在方法上,和类上有效
@Retention(RetentionPolicy.RUNTIME) // Retention:表示我们的注解在什么时候还有效,运行时候有效
@Documented // 表示说我们的注解是否生成在java doc中
@Inherited // 表示子类可以继承父类的注解
@interface MyAnnotation {
}
使用 @interface
自定义注解时,自动继承了 java.lang.annotation.Annotation
接口
/**
* 自定义注解
*
* @author: 陌溪
* @create: 2020-03-28-22:57
*/
public class MateAnnotationDemo {
// 注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation(schools = {"大学"})
public void test(){
}
}
/**
* 定义一个注解
*/
@Target(value={ElementType.METHOD, ElementType.TYPE}) // target表示我们注解应用的范围,在方法上,和类上有效
@Retention(RetentionPolicy.RUNTIME) // Retention:表示我们的注解在什么时候还有效,运行时候有效
@Documented // 表示说我们的注解是否生成在java doc中
@Inherited // 表示子类可以继承父类的注解
@interface MyAnnotation {
// 注解的参数:参数类型 + 参数名()
String name() default "";
int age() default 0;
// 如果默认值为-1,代表不存在
int id() default -1;
String[] schools();
}
动态语言是一类在运行时可以改变其结构的语言:例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其它结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
主要的动态语言有:Object-c、C#、JavaScript、PHP、Python等
与动态语言相比,运行时结构不可变的语言就是静态语言。例如Java、C、C++
Java不是动态语言,但是Java可以称为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制来获取类似于动态语言的 特性,Java的动态性让编程的时候更加灵活。
Java Reflection:Java反射是Java被视为动态语言的关键,反射机制运行程序在执行期借助于Reflection API 去的任何类内部的信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName("java.lang.String")
在加载完类后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射
tip:反射可以获取到private修饰的成员变量和方法
我们下面通过Class.forName来获取一个实例对象
/**
* 反射Demo
*
* @author: 陌溪
* @create: 2020-03-29-8:21
*/
public class ReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射获取类的Class对象
Class c1 = Class.forName("com.moxi.interview.study.annotation.User");
Class c2 = Class.forName("com.moxi.interview.study.annotation.User");
Class c3 = Class.forName("com.moxi.interview.study.annotation.User");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
/**
* 实体类:pojo,entity
*/
class User {
private String name;
private int id;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
上面我们通过反射获取了三个对象,我们输出对应对象的hashcode码,会发现
1173230247
1173230247
1173230247
它们的hashcode码是一样的,这就说明了:
在Object类中定义了以下的方法,此方法将被所有子类继承
public final Class getClass()
以上方法的返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
也就是说,我们通过对象来获取到它的Class,相当于逆过程
通过对照镜子我们可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了那些接口。对于每个类而言,JRE都为其保留一个不变的Class类型对象,一个CLass对象包含了特定某个结构的有关信息
/**
* Class类创建的方式
*
* @author: 陌溪
* @create: 2020-03-29-9:56
*/
class Person {
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student() {
this.name = "学生";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "老师";
}
}
public class ClassCreateDemo {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:" + person.name);
// 方式1:通过对象获得
Class c1 = person.getClass();
System.out.println("c1:" + c1.hashCode());
//方式2:通过forName获得
Class c2 = Class.forName("com.moxi.interview.study.annotation.Student");
System.out.println("c2:" + c2.hashCode());
// 方式3:通过类名获取(最为高效)
Class c3 = Student.class;
System.out.println("c3:" + c3.hashCode());
// 方式4:基本内置类型的包装类,都有一个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4.getName());
// 方式5:获取父类类型
Class c5 = c1.getSuperclass();
}
}
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
/**
* 获取Class的方式
*
* @author: 陌溪
* @create: 2020-03-29-10:16
*/
public class GetClassDemo {
public static void main(String[] args) {
Class c1 = Object.class; // 类
Class c2 = Comparable.class; // 接口
Class c3 = String[].class; // 数组
Class c4 = int[][].class; // 二维数组
Class c5 = Override.class; // 注解
Class c6 = ElementType.class; // 枚举
Class c7 = Integer.class; // 基本数据类型
Class c8 = void.class; // void,空数据类型
Class c9 = Class.class; // Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
}
最后运行结果为:
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
同时需要注意,只要类型和维度一样,那就是同一个Class对象
int [] a = new int[10];
int [] b = new int[10];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
这两个的hashcode是一样的
java内存分为以下三部分
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤对该类进行初始化:
java.lang.Class
对象。下面一段代码,分别说明了static代码块,以及子类和父类构造方法的执行流程
/**
* 类加载流程
*
* @author: 陌溪
* @create: 2020-03-29-11:02
*/
class SuperA {
static {
System.out.println("父类静态代码块初始化");
}
public SuperA() {
System.out.println("父类构造函数初始化");
}
}
class A extends SuperA{
static {
System.out.println("静态代码块初始化");
m = 300;
}
static int m = 100;
public A() {
System.out.println("A类的无参构造方法");
}
}
public class ClassLoaderDemo {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
}
}
最后的结果为:
父类静态代码块初始化
静态代码块初始化
父类构造函数初始化
A类的无参构造方法
100
说明静态代码块都是执行的,并且父类优先
加载到内存,会产生一个类对应Class对象
链接,链接结束 m = 0
初始化:
<clinit>() {
syso("A类静态方法")
m = 300;
m = 100;
}
java.lang.Class
对象,作为方法区中类数据的访问入口。类加载器作用是用来把类(Class)装载进内存的,JVM规范定义了如下类型的类的加载器
代码如下:
/**
* 类加载器的种类
*
* @author: 陌溪
* @create: 2020-03-29-11:51
*/
public class ClassLoaderTypeDemo {
public static void main(String[] args) {
//当前类是哪个加载器
ClassLoader loader = ClassLoaderTypeDemo.class.getClassLoader();
System.out.println(loader);
// 获取系统类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
// 获取系统类加载器的父类加载器 -> 扩展类加载器
ClassLoader parentClassLoader = classLoader.getParent();
System.out.println(parentClassLoader);
// 获取扩展类加载器的父类加载器 -> 根加载器(C、C++)
ClassLoader superParentClassLoader = parentClassLoader.getParent();
System.out.println(superParentClassLoader);
// 测试JDK内置类是谁加载的
ClassLoader loader2 = Object.class.getClassLoader();
System.out.println(loader2);
}
}
运行结果:我们发现,根加载器我们无法获取到
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@45ee12a7
null
null
获取类加载器能够加载的路径
// 如何获取类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
最后输出结果为:
// 如何获取类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
/*
E:\Software\JDK1.8\Java\jre\lib\charsets.jar;
E:\Software\JDK1.8\Java\jre\lib\deploy.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\access-bridge-64.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\cldrdata.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\dnsns.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\jaccess.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\jfxrt.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\localedata.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\nashorn.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\sunec.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\sunjce_provider.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\sunmscapi.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\sunpkcs11.jar;
E:\Software\JDK1.8\Java\jre\lib\ext\zipfs.jar;
E:\Software\JDK1.8\Java\jre\lib\javaws.jar;
E:\Software\JDK1.8\Java\jre\lib\jce.jar;
E:\Software\JDK1.8\Java\jre\lib\jfr.jar;
E:\Software\JDK1.8\Java\jre\lib\jfxswt.jar;
E:\Software\JDK1.8\Java\jre\lib\jsse.jar;
E:\Software\JDK1.8\Java\jre\lib\management-agent.jar;
E:\Software\JDK1.8\Java\jre\lib\plugin.jar;
E:\Software\JDK1.8\Java\jre\lib\resources.jar;
E:\Software\JDK1.8\Java\jre\lib\rt.jar;
C:\Users\Administrator\Desktop\LearningNotes\校招面试\JUC\Code\target\classes;
C:\Users\Administrator\.m2\repository\org\projectlombok\lombok\1.18.10\lombok-1.18.10.jar;
C:\Users\Administrator\.m2\repository\cglib\cglib\3.3.0\cglib-3.3.0.jar;
C:\Users\Administrator\.m2\repository\org\ow2\asm\asm\7.1\asm-7.1.jar;
E:\Software\IntelliJ IDEA\IntelliJ IDEA 2019.1.2\lib\idea_rt.jar
*/
我们能够发现,类在加载的时候,都是有自己的加载区域的,而不是任何地方的类都能够被加载
通过反射能够获取运行时类的完整结构
/**
* 获取运行时类信息
* @author: 陌溪
* @create: 2020-03-29-12:13
*/
public class GetClassInfo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class clazz = Class.forName("com.moxi.interview.study.annotation.User");
// 获取类名字
System.out.println(clazz.getName()); // 包名 + 类名
System.out.println(clazz.getSimpleName()); // 类名
// 获取类属性
System.out.println("================");
// 只能找到public属性
Field [] fields = clazz.getFields();
// 找到全部的属性
Field [] fieldAll = clazz.getDeclaredFields();
for (int i = 0; i < fieldAll.length; i++) {
System.out.println(fieldAll[i]);
}
// 获取指定属性的值
Field name = clazz.getDeclaredField("name");
// 获取方法
Method [] methods = clazz.getDeclaredMethods(); // 获取本类和父类的所有public方法
Method [] methods2 = clazz.getMethods(); // 获取本类所有方法
// 获得指定方法
Method method = clazz.getDeclaredMethod("getName", null);
// 获取方法的时候,可以把参数也丢进去,这样因为避免方法重载,而造成不知道加载那个方法
Method method2 = clazz.getDeclaredMethod("setName", String.class);
}
}
如果我们想定义一个:java.lang.string 包,我们会发现无法创建
因为类在加载的时候,会逐级往上
也就是说当前的系统加载器,不会马上的创建该类,而是将该类委派给 扩展类加载器,扩展类加载器在委派为根加载器,然后引导类加载器去看这个类在不在能访问的路径下,发现 sring包已经存在了,所以就无法进行,也就是我们无法使用自己自定义的string类,而是使用初始化的stirng类
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
采用双亲委派的一个好处是比如加载位于rt.jar 包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object 对象
创建类的对象:通过调用Class对象的newInstance()方法
如果没有无参构造器就不能创建对象?
只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
步骤如下:
通过反射,调用类中的方法,通过Method类完成。
完整代码:
/**
* 通过反射获取对象
*
* @author: 陌溪
* @create: 2020-03-29-12:43
*/
public class GetObjectByReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 获取Class
Class clazz = Class.forName("com.moxi.interview.study.annotation.User");
// 构造一个对象,newInstance调用的是无参构造器,如果没有无参构造器的话,本方法会出错
// User user = (User)clazz.newInstance();
// 获取class的有参构造器
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("小溪", 10, 10);
System.out.println(user2);
// 通过反射调用普通构造方法
User user3 = (User)clazz.newInstance();
// 获取setName 方法
Method setName = clazz.getDeclaredMethod("setName", String.class);
// 执行setName方法,传入对象 和 参数
setName.invoke(user3, "小白");
System.out.println(user3);
System.out.println("============");
Field age = clazz.getDeclaredField("age");
// 关闭权限检测,这样才能直接修改字段,因为 set方法不能直接操作私有变量
age.setAccessible(true);
age.set(user3, 10);
System.out.println(user3);
}
}
运行结果
User{name='小溪', id=10, age=10}
User{name='小白', id=0, age=0}
============
User{name='小白', id=0, age=10}
下面我们编写代码来具体试一试,使用反射的时候和不适用反射,在执行方法时的性能对比
/**
* 反射性能
*
* @author: 陌溪
* @create: 2020-03-29-14:55
*/
public class ReflectionPerformance {
/**
* 普通方式调用
*/
public static void test01() {
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行10亿次getName的时间:" + (endTime - startTime) + " ms");
}
/**
* 反射方式调用
*/
public static void test02() throws Exception {
Class clazz = Class.forName("com.moxi.interview.study.annotation.User");
Method getName = clazz.getDeclaredMethod("getName", null);
User user = (User) clazz.newInstance();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次getName的时间:" + (endTime - startTime) + " ms");
}
/**
* 反射方式调用,关闭权限检查
*/
public static void test03() throws Exception {
Class clazz = Class.forName("com.moxi.interview.study.annotation.User");
Method getName = clazz.getDeclaredMethod("getName", null);
User user = (User) clazz.newInstance();
long startTime = System.currentTimeMillis();
getName.setAccessible(true);
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user, null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次getName的时间:" + (endTime - startTime) + " ms");
}
public static void main(String[] args) throws Exception {
test01();
test02();
test03();
}
}
运行结果:
普通方式执行10亿次getName的时间:3 ms
反射方式执行10亿次getName的时间:2554 ms
反射方式执行10亿次getName的时间:1365 ms
我们上面分别是执行了 10亿次 getName的方法,从里面可以看出,通过直接实例化对象后,调用getName耗时最短,同时关闭了 权限检查后的比不关闭能提高一倍的性能。
Java采用泛型擦除机制来引入泛型,Java中的泛型仅仅是给编译器Java才使用的,确保数据的安全性和免去强制类型转换的问题,但是一旦编译完成后,所有的泛型有关的类型全部被擦除
为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是有何原始类型齐名的类型。
下面我们通过代码来获取方法上的泛型,包括参数泛型,以及返回值泛型
/**
* 通过反射获取泛型
*
* @author: 陌溪
* @create: 2020-03-29-15:15
*/
public class GenericityDemo {
public void test01(Map<String, User> map, List<User> list) {
System.out.println("test01");
}
public Map<String, User> test02() {
System.out.println("test02");
return null;
}
public static void main(String[] args) throws Exception{
Method method = GenericityDemo.class.getMethod("test01", Map.class, List.class);
// 获取所有的泛型,也就是参数泛型
Type[] genericParameterTypes = method.getGenericParameterTypes();
// 遍历打印全部泛型
for (Type genericParameterType : genericParameterTypes) {
System.out.println(" # " +genericParameterType);
if(genericParameterType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
// 获取返回值泛型
Method method2 = GenericityDemo.class.getMethod("test02", null);
Type returnGenericParameterTypes = method2.getGenericReturnType();
// 遍历打印全部泛型
if(returnGenericParameterTypes instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) returnGenericParameterTypes).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
得到的结果
# java.util.Map<java.lang.String, com.moxi.interview.study.annotation.User>
class java.lang.String
class com.moxi.interview.study.annotation.User
# java.util.List<com.moxi.interview.study.annotation.User>
class com.moxi.interview.study.annotation.User
###################
class java.lang.String
class com.moxi.interview.study.annotation.User
通过反射能够获取到 类、方法、字段。。。等上的注解
ORM即为:Object relationship Mapping,对象关系映射
下面使用代码,模拟ORM框架的简单使用
/**
* ORMDemo
*
* @author: 陌溪
* @create: 2020-03-29-15:33
*/
@TableKuang("db_student")
class Student2 {
@FieldKuang(columnName = "db_id", type="int", length = 10)
private int id;
@FieldKuang(columnName = "db_age", type="int", length = 10)
private int age;
@FieldKuang(columnName = "db_name", type="varchar", length = 10)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
/**
* 自定义注解:类名的注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableKuang {
String value();
}
/**
* 自定义注解:属性的注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldKuang {
String columnName();
String type();
int length() default 0;
}
public class ORMDemo {
public static void main(String[] args) throws Exception{
// 获取Student 的 Class对象
Class c1 = Class.forName("com.moxi.interview.study.annotation.Student2");
// 通过反射,获取到全部注解
Annotation [] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// 获取注解的value值
TableKuang tableKuang = (TableKuang)c1.getAnnotation(TableKuang.class);
String value = tableKuang.value();
System.out.println(value);
// 获得类指定的注解
Field f = c1.getDeclaredField("name");
FieldKuang fieldKuang = f.getAnnotation(FieldKuang.class);
System.out.println(fieldKuang.columnName());
System.out.println(fieldKuang.type());
System.out.println(fieldKuang.length());
}
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )