Java基础——日志、枚举、类加载器、反射

一、日志

 1.程序日志:用来记录应用程序的运行信息、状态信息、错误信息等,类似生活中的日记。

 2.日志作用:数据追踪、性能优化、问题排查、系统监控。

 3.日志框架:

        1)JUL:这是javaSE平台提供的官方日志框架,也被称为JUL,配置相对简单,但是不够灵活性能比较差

        2)Log4j:一个流行的日志框架,提供灵活的配置选项,支持多重输出目标

        3)Logback:基于Log4j升级而来,提供了更多的功能和配置选项,性能优于Log4j

        4)Slf4j(Simple Logging Facade for Java):简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架,Log4j和Logback是它的实现类

 4.Logback的快速入门:

        1)引入jar包

                资源: 百度网盘 请输入提取码 提取码: mw5g

                在项目中建立一个lib文件夹,放入,右键选择Add as Library…

        2)导入配置文件

                将网盘中的logback.xml文件放到项目的src文件夹(只能是这个文件夹下)下面

        3)获取日志对象使用

public static void main(String[] args) {
      Logger logger = LoggerFactory.getLogger("Main.class");
      logger.info("打印一下");
    }

二、枚举

 1.介绍:枚举是java中的一种特殊类型,常用于信息的标志和分类。做标记和分类可以通过常量和枚举两个方法,但是对比下来还是枚举的入参更严谨,提示性更强,代码更优雅。

 2.格式:enum 枚举类名{枚举1,枚举2,}  enum Season { season1,season2}

 3.枚举特点: 

public class Main {
    public static void main(String[] args) {
        //特点2:通过枚举类名去访问指定的枚举项 Flag.FAIL
        dome(Flag.FAIL);
        //特点1:每一个枚举项其实就是该枚举的一个对象
        boolean fail = Flag.FAIL.equals("FAIL");
        //特点3:所有枚举类都是Enum的子类,可以通过API查询
        int ordinal = Flag.FAIL.ordinal();
        System.out.println(ordinal);
    }
    private static void dome(Flag flag){
        switch (flag){
            case FAIL:
                System.out.println("失败");
                break;
            case WAIT:
                System.out.println("等待");
                break;
            case SUCCESS:
                System.out.println("成功");
                break;
            case PROGRESS:
                System.out.println("进行中");
                break;
        }

    }
}
enum Flag{
    //特点5:枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的
    WAIT("等待") ,PROGRESS("进行中"),SUCCESS("成功"),FAIL("失败");
    //特点4:枚举也是类,可以定义成员变量
    String name;
    //特点6:枚举类可以有构造器,但必须是private 的,它默认也是private(了解,基本不用)
   private Flag(String name){
       this.name = name;
       System.out.println(this.name);
   }
   //特点7:枚举类也可以有抽象方法,但是枚举项必须重写该方法(了解,基本不用)
//   public abstract void show();
//   要写在前面的
//    WAIT("等待") {
//        @Override
//        public void show() {
//
//        }
//    }
}

 4.枚举使用场景:  用来做信息的标志和分类

三、类加载器

 1.介绍:将类的字节码载入方法区中

2.加载时机:用到就会加载 

3.加载过程:

        1)加载:

                通过包名+类名,获取这个类,准备用流进行传输
                将类加载到内存中
                加载完毕创建-一个class 对象,Class中包含成员变量、成员方法

       2) 连接

                ①验证:验证类是否符合JVM规范,安全性检查

                ②准备:为static 变量分配空间,设置默认值(只是准备并没有赋值)

                ③解析:将常量池中的符号引用解析为直接引用

                观察:看看C类有没有加载,如果没有加载,成员变量的C是啥… 

public class Hsdb {
    public static void main(String[] args) throws IOException {
        //加载B类的加载器
        ClassLoader bc = B.class.getClassLoader();
        //观察创建B类和不创将B类时类C在常量池中的变化
        //new B()
        //阻止程序结束,保留进程
        System.in.read();
    }
}
class B{
    C c = new C();
}
class C{

}

     ​​​​​找到JDK8 进入文件夹,调出命令行,执行jps查看进程id,在运行java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB (JDK 8版本以后,HSDB被取消),点击左上角的file按钮然后点击第一个,在输入进程id进行查询,然后点击tools按钮后选择Class Browser浏览类的信息,找到对应的B类,点击进入找到Constant Pool常量池点击,观察C类的值。(会发现执行完new B()之后C在常量池中变成了一串地址,这就是地址引用)

 

       3) 初始化

                 根据程序员程序编码制定的主观计划去初始化类变量和其他资源(就是将静态变量附上值)

4.分类: (getClassLoader()获取类加载器对象)

BootStrap class loader:虚拟机内置类加载器,通常表示null,是C++实现的,获取到的只能是null

Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块,负责加载lib/modules内部类(这个类是从JDK9开始的,JDK9之前是: (Extension Class Loader) 扩展类加载器负责加载jre\lib\ext 目录下的类)

Application class loader:负责加载自己写的类 

自定义类加载器:上级为 Application

public static void main(String[] args) {
        //BootStrap class loader:加载JDK自带的类
       ClassLoader classLoader = String.class.getClassLoader();
       System.out.println(classLoader);
        //Platform class loader:平台加载类 lib\modules
        //Application class loader:系统类加载器 加载自己写的类
        ClassLoader classLoader1 = loaderText.class.getClassLoader();
        System.out.println(classLoader1);
        //加载上下级关系
        System.out.println(classLoader1.getParent());
        System.out.println(classLoader1.getParent().getParent());
    }

5.双亲委派模式(避免类的重复加载)

如果一个类加载器收到了类加载请求,它并不会自己先去加载
而是把这个请求委托给父类的加载器去执行
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归
请求最终将到达顶层的启动类加载器
如果父类加载器可以完成类加载任务,就成功返回
倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式 

 

6.ClassLoader 的常用成员方法

方法功能
public static ClassLoader getSystemClassLoader( )获取系统类加载器
public InputStream getResourceAsStream(String name )加载某一个资源文件
public static void main(String[] args) throws IOException {
        //获取系统加载器 Application Class Loader
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //通过系统类加载器,加载配置文件
        //系统类加载器加载我们自己编写的类,自己编写的类都在SRC下,可以通过文件名直接找到
        InputStream resourceAsStream = systemClassLoader.getResourceAsStream("config.properties");
        Properties properties = new Properties();
        properties.load(resourceAsStream);
        resourceAsStream.close();
        final String name = properties.getProperty("name");
        System.out.println(name);
    }

四、反射

 1.介绍

        框架技术的灵魂
        是在运行状态中,对于任意-个类, 都能够知道这个类的所有属性和方法
        对于任意一个对象, 都能够调用它的任意属性和方法
        这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制 

2.学习路径

        注:所有带Declared的暴力返回方法都不建议使用

        1)获取类的字节码对象

                Class.forName("全类名"), 类名.class,对象.getClass(); 

public class domeOne {
    public static void main(String[] args) throws ClassNotFoundException {
        //Class.forName("全类名")
        Class<?> aClass = Class.forName("domin.Student");
        // 类名.class
        Class<Student> studentClass = Student.class;
        // 对象.getClass();
        Student student = new Student();
        Class<? extends Student> aClass1 = student.getClass();
        System.out.println(aClass);
        System.out.println(studentClass);
        System.out.println(aClass1);

        //类的自解码文件只有一份
        System.out.println(aClass == aClass1);
        System.out.println(aClass == studentClass);
        System.out.println(aClass1 == studentClass);
    }
}

        2)反射类中的构造方法,随后创建对象
        构造方法:

方法功能
Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组
Constructor<T> getConstructor(Class<?>... parameterTypes)返回单个公共构造方法对象
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)返回单个构造方法对象

         Constructor类用于创建对象的方法:

方法功能
T newInstance(Object..initargs)根据指定的构造方法创建对象
setAccessible(boolean flag)设置为true,表示取消访问检查
public class domeTow {
    public static void main(String[] args) throws Exception {
        //1.获取自解码文件
        Class<?> aClass = Class.forName("domin.Student");
        //2.获取构造方法
        //getConstructors只能返回带有public修饰的构造方法(基本不用,因为没有针对性)
        Constructor<?>[] cs = aClass.getConstructors();
        for (Constructor<?> constructor : cs) {
            System.out.println(constructor);
        }
        System.out.println("===================================");
        //getDeclaredConstructors返回所有方法,又称暴力返回(基本不用,因为没有针对性)
        Constructor<?>[] ds = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : ds) {
            System.out.println(declaredConstructor);
        }
        System.out.println("===================================");
        //getConstructor只能返回带有public修饰的构造方法,返回不带public的会报错
        Constructor<?> c = aClass.getConstructor();
        System.out.println(c);
        System.out.println("===================================");
        //getDeclaredConstructor返回所有方法,又称暴力返回
        Constructor<?> dC = aClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(dC);
        //暴力返回虽然能返回方法,但是没发用,还是需要通过setAccessible设置权限才能进行使用
        dC.setAccessible(true);
        Object use = dC.newInstance("张三", 23);
        System.out.println(use);

    }
}

        3)反射类中的成员变量,完成赋值和获取

方法功能
Field[] getFields()返回所有公共成员变量对象的数组
Field[] getDeclaredFields()返回所有成员变量对象的数组
Field getField(String name)返回单个公共成员变量对象
Field getDeclaredFields(String name)返回单个成员变量对象

             Field类的设置和获取方法

方法功能
void set(Object obj,Object value)赋值
Object get(Object obj)获取值
public class domeThree {
    public static void main(String[] args) throws Exception {
        //1.获取自解码文件
        Class<?> aClass = Class.forName("domin.Student");
        //2.获取类中的成员变量

        //获取成员变量getFields只能获取public
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("=======================");
        //获取全部成员变量
        Field[] decFields = aClass.getDeclaredFields();
        for (Field decField : decFields) {
            System.out.println(decField);
        }
        System.out.println("=======================");
        //获取成员变量getField只能获取public,指定成员变量的名字,如果成员变量不是public会报错
//        Field field = aClass.getField("name");
//        System.out.println("field");
        Field nf = aClass.getDeclaredField("name");
        Field af = aClass.getDeclaredField("age");
        System.out.println(nf+"____"+af);
        //暴力返回需要设置权限
        nf.setAccessible(true);
        af.setAccessible(true);
        Constructor<?> c = aClass.getConstructor();
        Object stu1 = c.newInstance();
        Object stu2 = c.newInstance();

        nf.set(stu1,"lili");
        nf.set(stu2,"mimi");

        af.set(stu1,20);
        af.set(stu2,18);
        System.out.println(stu1);
        System.out.println(stu2);
        Object stu1n = nf.get(stu1);
        Object stu2a = af.get(stu2);
        System.out.println(stu1n);
        System.out.println(stu2a);
    }
}

         4)反射类中的成员方法,调用成员方法 

方法说明
Method[] getMethods()返回所有公共成员方法对象的数据组,包括继承的
Method[] getDeclareMethods()返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name,Class<?>... parameterTypes)返回单个公共成员方法对象
Method getDeclaredMethod(String name,Class<?>... parameterTypes)返回单个成员方法对象

 Method类用于执行方法的方法

方法说明
Object invoke(Object obj,Object... args)运行方法

需求:请向-一个泛型为Integer 的集合,添加一个String字符串 

/*
* 需求:请向-一个泛型为Integer 的集合,添加一个String字符串
*思路: Java中的泛型是假的,只在编译的时候有效
* */
public class domeFour {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        final ArrayList<Integer> integers = new ArrayList<>();
        Collections.addAll(integers,1,2,3,4);

        //1.获取integers集合的字节码对象
        Class<? extends ArrayList> aClass = integers.getClass();
        //2.反射类中的add方法
        Method aF = aClass.getMethod("add", Object.class);
        //3.让add方法执行
        aF.invoke(integers,"ABC");
        System.out.println(integers);
    }
}

练习:

编写三个任意的Javabean类,Student类, Teacher类, Worker类。
属性均为: name, age
方法要求:
Student里面写学习study和吃饭eat的方法(方法无参无返回)
Teacher里面写上课teach和吃饭eat的方法(方法无参无返回)
Worker里面写工作work和睡觉sleep的方法(方法无参无返回)
本地新建配置文件properties
属性: className=Student类的全类名 methodName= study
在测试类中读取properties文件中的两个属性值
利用反射创建对象学生类的对象,并调用方法
修改配置文件,不修改代码,运行老师类中的上课方法
修改配置文件,不修改代码,运行工人类中的工作方法 

public class domeFive {
    public static void main(String[] args) throws Exception {
        //1.加载配置文件
        InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties");
        //2.创建一个Properties集合,加载数据
        Properties prop = new Properties();
        prop.load(resourceAsStream);
        resourceAsStream.close();
        //3.获取数据
        String name = prop.getProperty("className");
        String method = prop.getProperty("methodName");
        //4.获取字节码对象
        Class<?> aClass = Class.forName(name);
        //5.反射内部的构造方法并实例化
        final Object mF = aClass.getConstructor().newInstance();
        //6.反射内部的成员方法对象
        final Method method1 = aClass.getMethod(method);
        method1.invoke(mF);
    }
}
public class Student {
    private String name;
    private int age;
    public void study(){
        System.out.println("学生在学习");
    }
    public void eat(){
        System.out.println("学生在吃饭");
    }
    public Student() {
    }

     Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
//config.properties文件
className = domin.Student
methodName = study

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值