java基础知识点笔记总结(十一)(代码片段)

IT_Holmes IT_Holmes     2023-02-23     353

关键词:

1. 反射机制


什么是动态语言,什么是静态语言?


Reflection(反射),java因为有了反射机制,才让java有了一定的动态性。

正常我们创建的方式是:

引入需要的包类名称 ==》 new实例化 ==》取得实例化对象。

而反射方式恰恰相反:

通过实例化对象 ==》 调用getClass()方法 ==》 得到完整的包类名称。

上面的过程就像光的反射一样


反射的应用场景:

2. 创建Person类以及其他的一些注解接口等 (方便后面测试其他方法使用)


创建了一个公共的Person类,这个类也是往后测试用的类:

package exam.demo;

@MyAnnotation(value = "hi")
public class Person extends Creature<String> implements Comparable<String> , MyInterface

    private String name;
    int age;
    public int id;

    public Person()

    @MyAnnotation(value = "abc")
    public Person(String name)
        this.name = name;
    

    private Person(String name,int age)
        this.name = name;
        this.age = age;
    

    @MyAnnotation
    private String show(String nation)
        System.out.println("我的国际是:"+nation);
        return nation;
    

    public String display(String interests) throws Exception,NullPointerException
        return interests;
    

    @Override
    public void info() 
        System.out.println("我是一个人");
    

    @Override
    public int compareTo(String o) 
        return 0;
    

    private static void showDesc()
        System.out.println("hello ,world.");
    

    @Override
    public String toString() 
        return "Person" +
                "name='" + name + '\\'' +
                ", age=" + age +
                ", id=" + id +
                '';
    

Creature类:

package exam.demo;

import java.io.Serializable;

public class Creature<T> implements Serializable 
    private char gender;
    public double weight;

    private void breath()
        System.out.println("生物呼吸");
    

    public void eat()
        System.out.println("生物吃东西");
    

MyAnnotation注解:

package exam.demo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;

@Target(TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME) //注解必须用RUNTIME的生命周期
public @interface MyAnnotation 

    String value() default "hello";


MyInterface接口:

package exam.demo;
public interface MyInterface 
    void info();


整体上一个反射效果案例(了解,看后面):

package filefanxingTest;

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

import org.junit.Test;

public class ReflectionTest 

	// 反射之前对于Person类的操作
	@Test
	public void test1() 
		// 1.创建Person类的对象
		Person p1 = new Person("Tom", 12);
		// 2.通过对象,调用其内部的属性,方法
		p1.age = 10;
		System.out.println(p1.toString());
		p1.show();
		// 在Person外部,不可以通过Person类的对象调用其内部私有结构
		// 比如,我们定义的name,showNation()方法以及私有的构造器。
	

	// 反射之后,对于Person的操作
	@Test
	public void test2() throws Exception 

		// 1.通过反射创建Person类的对象
		Class clazz = Person.class;
		Constructor cons = clazz.getConstructor(String.class, int.class);
		Object obj = cons.newInstance("Tom", 12);
		Person p = (Person) obj;
		System.out.println(p.toString());

		// 2.通过反射 ,调用对象指定的属性和方法
		Field age = clazz.getDeclaredField("age");
		age.set(p, 10);
		System.out.println(p.toString());

		Method show = clazz.getDeclaredMethod("show");
		show.invoke(p);

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

		// 通过反射,是可以调用Person类的私有结构的,比如:私有的构造器,私有的方法,私有的属性。
		Constructor cons2 = clazz.getDeclaredConstructor(String.class);
		cons2.setAccessible(true);
		Person p2 = (Person) cons2.newInstance("Jerry");
		System.out.println(p2);

		// 调用私有属性
		Field name = clazz.getDeclaredField("name");
		name.setAccessible(true);
		name.set(p2, "zhangsan");

		// 调用私有的方法
		Method showNation = clazz.getDeclaredMethod("showNation", String.class);
		showNation.setAccessible(true);
		String nation = (String) showNation.invoke(p2, "中国");// 相当于String nation = p1.showNation("中国");
		System.out.println(nation);

	

反射可以给私有属性,私有方法,私有构造器来设置值。正常的new创建对象是不可以的!

3. 两个疑问(看自己理解)


通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底使用哪个呢?

建议:直接new的方式。

什么时候会使用:反射的方式? 反射的特征:动态性。


反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?

不矛盾,封装性提示的是你哪个方法能调用哪个方法不能调用(private),整体上算是一个限制提示建议的效果。

4. java.lang.Class 类的理解


类的加载过程:

程序经过javac.exe 以后,会生成一个或多个字节码文件(.class结尾)。

接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,这个过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。


提示小结:
Idea项目下的out目录是用来存放.java文件编译后的字节码文件的。


万事万物皆对象!
平常我们是对象.xxx属性 , File对象, URL对象等等。反射同样也是,就是通过Class的类对象,来实现的。

并且Class的实例就对应着一个运行时类,因此我们不能new一个Class,因为他是运行时类本身就存在的。

5. 如何获取Class实例的4中方式


首先,第一点Class运行时类并不是我们自己创建出来的,而是它编译运行后自己有的类。

加载到内存中的运行时类,会缓存一定的时间。在此事件之内,我们可以通过4中不同的方式来获取此运行时类。(也就是只有一个运行时类,只不过获取方式不同。)

package com.test;

import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;


public class ReflectionTest 

	@Test
	public void test3() throws ClassNotFoundException 

		//方式一:调用运行时类的属性:.class
		Class clazz1 = Person.class;
		System.out.println(clazz1);
		//也是可以加泛型的
		//Class<Person> clazz1 = Person.class;

		//方式二:通过运行时类的对象,调用getClass()来获取
		Person p1 = new Person();
		Class clazz2 = p1.getClass();
		System.out.println(clazz2);

		//方式三:调用Class的静态方法:forName(String classPath)
		Class clazz3 = Class.forName("com.test.Person");
		System.out.println(clazz3);

		//我们比较一下他们的地址值,返回都是true,说明他们都是相同的运行时类,只不过获取的方式不同而已。
		System.out.println(clazz1 == clazz2);//true
		System.out.println(clazz1 == clazz3);//true

		//方式四:使用类的加载器:ClassLoader
		ClassLoader classLoader = ReflectionTest.class.getClassLoader();
		Class clazz4 = classLoader.loadClass("com.test.Person");
		System.out.println(clazz4);

		System.out.println(clazz1 == clazz4);//true
	

6. Class实例可以是哪些结构?

package com.test;

import org.junit.jupiter.api.Test;

import java.lang.annotation.ElementType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;


public class ReflectionTest 
	@Test
	public void test4()
		Class c1 = Object.class;
		Class c2 = Comparable.class;
		Class c3 = String[].class;
		Class c4 = int[][].class;
		Class c5 = ElementType.class;
		Class c6 = Override.class;
		Class c7 = int.class;
		Class c8 = void.class;
		Class c9 = Class.class;

		int[] a = new int[10];
		int[] b = new int[100];
		Class c10 = a.getClass();
		Class c11 = b.getClass();
		//只要元素类型与维度一样,就同一个Class
		System.out.println(c10 == c11);//true
	


7. 类的加载 与 ClassLoader的理解

7.1 类的加载过程


当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。


这三个步骤必须牢记!

加载(Load):就是讲class文件字节码内容加载到内存中,转换为运行时数据结构,生成一个代码这个类的java.lang.Class对象。


链接(Link):正式为类变量(static类型)分配内存并设置类变量默认初始值,例如在static int a 在链接这个环节它就会被赋值默认初始值0,String b在链接就被默认赋值为null。


初始化:这个阶段有一个类构造器<clinit>()方法,非常重要,它是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生。

当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

7.2 类加载器(ClassLoader)的作用


类加载器的作用和类缓存:


类加载器作用就是用来把类(class)装载进内存的。

我们平时定义的类,都是通过系统类加载器来架子啊的,因此也算是最常用的加载器了。

package com.test;

import org.junit.jupiter.api.Test;

public class ClassLoadingTest 
    @Test
    public void test1()
        //对于定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoadingTest.class.getClassLoader();
        System.out.println(classLoader);//这个就是AppClassLoader系统加载器

        //调用系统类加载的getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);//这个就是ExtClassLoader拓展加载器

        //调用扩展类加载器的getParent(): 无法直接获取引导类加载器的。
        //引导类加载器主要负责加载java的核心类库,它是无法加载自定义类。
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//这里返回null,并不是没有,而是引导类加载器不能直接获取。

        //引导类加载器加载java的核心类库,例如String类型
        ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3);//这里也是返回null,说明String的加载器也是引导类加载器。
    

7.3 类加载器(ClassLoader) 加载配置文件Properties案例


我们在jdbc场景下,就有两种加载Properties文件的方式:

  • 读取配置文件方式一:使用FileInputStream方法,此路径默认是当前module工程模块下。
  • 读取配置文件方式二:使用ClassLoader , 此路径默认是当前module的src下。
package com.test;

import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

public class ClassLoadingTest 

    /*
        Properties:用来读取配置文件。
    */
    @Test
    public void test2() throws Exception 

        Properties pros = new Properties();
        //读取配置文件方式一:使用FileInputStream方法,此路径默认是当前module工程模块下。
        FileInputStream fis = new FileInputStream("jdbc.properties");

        //如果想用这种方式访问jdbc1.properties也是可以的,就是需要配置路径如下
        //FileInputStream fis2 = new FileInputStream("src\\\\jdbc1.properties");
        pros.load(fis);
        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("姓名:"+user + ", 密码:" +password);

    

    @Test
    public void test3() throws Exception 

        Properties pros = new Properties();
        //读取配置文件方式二:使用ClassLoader , 此路径默认是当前module的src下。
        ClassLoader classLoader = ClassLoadingTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("姓名:"+user + ", 密码:" +password);

    

8. 反射 newInstance()方法 创建对应的运行时类的对象


clazz.newInstance()方法:调用此方法,创建对应的运行时类的对象。该方法默认调用的就是类的空参构造器。

因此要想此方法能正常创建运行时类的对象,要求:

  • 1.运行时类必须提供空参的构造器。
  • 2.空参的构造器的访问权限得够!通常设置为public。

一个public的空参构造器,在javabean中很重要,原因如下:

  • 1.便于反射,创建运行时类的对象。
  • 2.便于子类继承此运行时类 时,默认调用super()时,保证父类有此构造器。

package com查看详情  

java基础知识点笔记总结(代码片段)

文章目录1.面向对象1.1类和实例1.2类的属性和方法1.3对象的内存解析1.4对象数组1.5匿名对象1.6方法重载(overload)loading...1.7可变个数形参1.8值传递的注意细节1.9递归(recursion)方法2.面向对象封装隐藏3.构造器(构造方法,constructor)4.... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.static关键字1.1static修饰属性1.2static内存存储解析1.3static修饰方法1.4开发中,如何判断是否设置为静态属性或方法2.单例(Singleton)设计模式3.Main方法4.代码块5.final关键字6.抽象类和抽象方法7.匿名类8.模板方法设计模式9... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.eclipse操作2.数组定义和概述3.数组静态初始化和动态初始化4.获取数组长度和遍历数组5.不同类型数组的默认值6.数组的内存解析7.多维数组8.二维数组获取长度和遍历数组9.多维数组的默认值10.二维数组的内存解析11.数... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.String特性2.String的字面量创建和new对象形式3.String字符串内存存储原理4.String不同拼接对比效果5.String和数组常用的混合面试题6.JVM涉及字符串内存结构7.String常用方法8.涉及到String类与其他结构之间的转换8.1常见的小错... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.算术运算符2.算术运算符3.比较运算符4.逻辑运算符5.位运算符6.交换数据的三种方式7.三元运算符8.运算符优先级9.流程控制10.分支结构(流程控制)-if...else语句11.使用Scanner从键盘中获取不同类型的数据12.分支结构(流程控... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.泛型定义2.泛型的小细节3.泛型自定义泛型4.泛型自定义泛型注意点5.泛型方法6.泛型继承注意点7.泛型通配符?8.泛型有限制条件的通配符?9.File类9.1File类的使用9.2file类的常用方法9.3file类总结10.IO流11.IO流体系对... 查看详情

关于java反射基础知识/编码经验的一些总结(代码片段)

写在前面温习一下毕业以来学习的东西。准备做成一个系列。所以对于每一部分技术点进行一个笔记整理。更多详见java面试的一些总结笔记主要是以网上开源的一本《Java核心面试知识整理》面试笔记为原型,结合工作中学... 查看详情

关于java异常基础知识/编码经验的一些总结(代码片段)

写在前面温习一下毕业以来学习的东西。准备做成一个系列。所以对于每一部分技术点进行一个笔记整理。更多详见java面试的一些总结笔记主要是以网上开源的一本《Java核心面试知识整理》面试笔记为原型,结合工作中学... 查看详情

java万字长文基础知识总结(下)-王者笔记《收藏版》(代码片段)

上一篇Java基础知识学习总结之(上)   下一篇Java集合容器篇面试题 (上)                                          目录三、计算机原理和操作系统 内存、CPU、硬盘? Linux基本命令... 查看详情

java万字长文基础知识总结(下)-王者笔记《收藏版》(代码片段)

上一篇Java基础知识学习总结之(上)                                             目录三、计算机原理和操作系统 内存、CPU、硬盘? Linux基本命令线程和进程的区别?四、数据库基础关系... 查看详情

零基础学c语言知识总结十一:动态内存分配!(代码片段)

动态内存分配(动态存储期)在程序执行并使用该变量的时候分配内存空间,使用完毕立即释放.动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存... 查看详情

零基础学c语言知识总结十一:c语言的内存四区(代码片段)

一个正在运行着的C编译程序占用的内存分为代码区、静态数据区、未初始化数据区、堆区和栈区5个部分。C语言中定义4个内存区间是: 代码区, 静态存储区, 栈区,堆区. 其中栈区和堆区是属于动态存储区​可执行文件在存储... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.数组与集合概述2.Collection接口和Map接口集合3.Collection接口集合3.1Collection接口方法3.2集合转数组和数组转集合的一些方法4.Iterator遍历4.1使用Iterator遍历Collection4.2使用foreach循环遍历集合元素5.Collection子接口之一list接口5.1... 查看详情

《java程序设计》第十一周学习总结(代码片段)

《JAVA程序设计》第十一周学习总结一、目录:第十三章知识总结上周错题代码托管PSP二、第十三章知识总结:Java网络编程URL类是java.net包中的一个重要的类,URL的实例封装着一个统一资源定位符,使用URL创建对象的应用程序称... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.枚举类介绍2.JDK5.0之前自定义枚举类3.JDK5.0之后使用enum关键字定义枚举类4.enum类中的常用方法5.使用enum关键字定义的枚举类实现接口的情况6.注解(Annotation)7.注解起源功能8.三个JDK内置基本注解8.1@Override注解8.2@Depre... 查看详情

java基础知识点笔记总结(代码片段)

文章目录1.泛型定义2.泛型的小细节3.泛型自定义泛型4.泛型自定义泛型注意点5.泛型方法6.泛型继承注意点7.泛型通配符?8.泛型有限制条件的通配符?9.File类9.1File类的使用9.2file类的常用方法9.3file类总结10.IO流11.IO流体系对... 查看详情

java基础知识总结(代码片段)

...了,正好做项目的时候,有个功能,几乎囊括了所有基础知识点,所以把这段自己写的代码记录下来,算是反馈学习成果。 二、代码   1publicclassUdnsAclCommitServiceImplimplementsIUdnsAclCommitService23//创建LOGGE 查看详情