java学习——java基础(2)

一点关于java基础概念的学习笔记,都是些很基础的概念,如果有不对的地方欢迎指正,谢谢!

上一篇传送门点我

谈谈Java的反射机制

反射是Java语言的一个关键特性,它允许程序在运行时动态地获取类的信息(成员变量、成员方法、构造方法)并操作类的属性和方法。
反射机制的工作原理:当Java程序在运行时加载某个类时,Java虚拟机(JVM)会解析类的字节码文件,为该类创建一个Class对象,并将类的所有信息存储在这个Class对象中。程序可以通过获取这个Class对象来访问类的信息,并进而操作类的属性和方法
反射的作用:
1.获取一个类里面所有的信息,获取到了之后,再去执行其他的业务逻辑。
2.结合配置文件,动态的创建对象并调用方法。

在这里插入图片描述

什么是动态代理?

Java动态代理是Java语言提供的一种机制,这种机制能在运行时动态地生成代理类和代理对象,进而替代原始的对象行为。通过动态代理,开发者可以在不修改原始类代码的情况下,对原始类的方法进行功能增强或添加额外的操作。它是实现面向切面编程(AOP)的一种重要手段。

动态代理的实现原理是,在运行时动态生成一个代理类,这个代理类继承了被代理类的接口,并重写被代理类的方法。当客户调用被代理类的方法时,实际上是通过代理对象来调用的。代理对象在调用被代理类的方法之前或之后,可以执行一些额外的逻辑,如权限验证、日志记录、性能监控等。

泛型中的extends和super

1.<? extends T> 表示包括T在内的任何T的子类
2.<? super T> 表示包括T在内的任何T的父类

接口和抽象类的区别

1.首先,抽象类是对事物的抽象,表示对象是什么,而接口是对行为的抽象,表示对象能做什么
2.抽象类要被子类继承(extends),接口要被类实现(implement)。
3.抽象类中可以有方法声明、方法实现、构造方法,而接口只能有方法声明。
4.抽象类中的变量可以是普通变量,而接口中只能定义静态常量
5.一个类只能继承一个抽象类,但可以实现多个接口。

补充注意点:接口和抽象类都不可以被final修饰,因为被final修饰后的类就不能被实现或继承了,而接口和抽象类一定要被实现或继承。

谈谈jdk中的集合类

Collection是JDK中集合类型的上层接口,很多相关接口和集合类都派生于它(Map接口没有继承Collection)。主要的集合类有以下四类: List(列表)、Queue(队列) 、Set(集合 )、Map(键值对映射), 其中List和Queue 是有序的可重复的(Queue更适合描述具有先进先出特征的数据结构), Set 是不可重复 ,而Map 是无序的 ,键值唯一。

Integer和int的区别

1.Integer是int的包装类,int则是java的一种基本的数据类型;
2.Integer变量必须实例化之后才能使用,而int变量不需要实例化;
3.Integer实际是对象的引用,当new一个Integer时,实际上生成一个指针指向对象,而int则直接存储数值
4.Integer的默认值是null,而int的默认值是0。

谈谈深拷贝和浅拷贝

浅拷贝(浅克隆):在拷贝一个对象时,对对象的基本数据类型的成员变量进行拷贝,但对引用类型的成员变量只进行引用的传递,并没有创建一个新的对象,当对引用类型的内容修改会影响被拷贝的对象。
深拷贝(深克隆):在拷贝一个对象时,除了对基本数据类型的成员变量进行拷贝,对引用类型的成员变量进行拷贝时,创建了一个新的对象来保存引用类型的成员变量。
总结:浅拷贝复制引用类型变量只是通过引用传递拿来数据,深拷贝则是创建了新对象存数据。

hashCode()和与equals()的关系

equals()和hashCode()都是Object类中定义的方法。其中equals()比较的是两个对象的内存地址是否相等,而hashCode()返回的是对象的内存地址。所以hashCode主要是用于查找使用的,而equals()是用于比较两个对象是否相等的。
但是,两个对象通过hashCode()得到的hash值相等,并不能保证两个对象值一定相等,因为不同的对象的hash值有可能是相同的
所以equals()相等hashCode()一定相等,而hashCode()相等但是equals()不一定相等

重写equals()和hashCode()方法的作用是什么

重写equals()方法可以定制对象之间的相等比较逻辑
重写hashCode()方法可以保证对象在哈希表中的正确存储和查找

为什么重写equals()方法,就一定要重写hashCode()方法?

如果只重写equals()方法而不重写hashCode()方法,就可能会导致a.equals(b)这个表达式成立,但是hashCode()却不相同。这样的话,这个只重写了hashCode()的对象,在使用散列集合进行存储的时候就会出现问题,因为散列集合是使用hashCode来计算key的存储位置,如果存储两个完全数值完全相同的对象但是有不同的hashCode,就会导致这两个对象存储在hash表的不同位置,导致程序逻辑出现错误。

请简要介绍一下Java中的异常体系

Java中的所有异常都来自顶级父类Throwable
Throwable下有两个子类ExceptionError
Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
Exception不会导致程序停止,又分为两个部分RunTimeException运行时异常和CheckedException检查时异常。
RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过。

Java中的异常处理机制

Java使用try-catch-finally语句块来处理异常。
其中,try块用于包裹可能抛出异常的代码,try语句块中代码受异常监控,其中代码发生异常时,会抛出异常对象;
catch块用于捕获和处理异常,catch语句带一个Throwable类型的参数,表示可捕获异常类型。当 try中出现异常时,catch会捕获到发生的异常,并和自己的异常类型匹配,若匹配,则执行catch块中代码;
finally块用于定义无论是否异常都需要执行的代码(必须执行!!!)。

throw和throws关键字的作用和区别

throw关键字用于主动抛出一个异常对象,可以在任何地方使用
throws关键字用于在方法上声明可能抛出的异常类型,后面跟的是异常类,告诉调用者需要处理这些异常。

final、finally、finalize的区别

final 用于声明属性,方法和类,分别表示属性不可变,注意:如果是基本类型说明变量本身不能改变,如果是引用类型,说明它不能指向其他的对象了。但对象还是可以改变的。方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,finally只能用在try/catch语句中,并且是附带着的一个语句块,表示这段语句最终总是被执行。
finalize是Objec类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。(JVM内容)

什么时候需要用finally块?

finally块一般用于执行无论是否发生异常都需要执行的代码,它为异常出来提供了一个统一的出口,使得在控制流转到其它部分以前(即使有return,break语句),能够对程序的状态作统一的管理。finally程序块中通常都包含一些用于资源释放、关闭连接等功能的代码块。例如,在finally程序块中应将try程序块中打开的所有文件关闭。

finally块中的return语句会如何影响方法的返回值?

如果在finally块中使用了return语句,会覆盖在try块中使用的return语句,即最终以finally块中的return值为准。(finally块优先级最大)

Java中如何格式化时间?

在java8以前,可以用Date类来表示时间,对于Date类,格式化时间可以使用SimpleDateFormat类来实现。代码逻辑如下:

        // 创建一个Date对象获取当前时间  
        Date date = new Date();  
        // 创建一个SimpleDateFormat对象,定义时间格式  
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        // 使用SimpleDateFormat对象格式化Date  
        String formattedDate = formatter.format(date);  

对于Date类的格式化,首先创建了一个 Date 对象,它表示当前的日期和时间。然后,再创建了一个 SimpleDateFormat 对象,并为其提供了一个时间格式字符串 “yyyy-MM-dd HH:mm:ss”,这个格式表示年-月-日 时:分:秒。最后使用 format 方法即可将 Date 对象格式化为指定格式的字符串。
需要注意的是,SimpleDateFormat 类在多线程环境中不是线程安全的。如果需要在多线程环境中进行日期格式化,还需要有进一步的策略去解决线程安全问题。
在Java8之后,引入了LocalDateTime这个新类型,这个类提供了更多的功能和灵活性。对于LocalDateTime类,可以使用DateTimeFormatter来进行时间的格式化。代码逻辑如下:

        // 创建一个LocalDateTime对象获取当前时间  
        LocalDateTime now = LocalDateTime.now();  
        // 创建一个DateTimeFormatter对象,定义时间格式  
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");  
        // 使用DateTimeFormatter对象格式化LocalDateTime  
        String formattedDateTime = now.format(formatter);

对于LocalDateTime类的格式化,首先创建一个LocalDateTime对象,该对象表示当前的日期和时间。然后,使用DateTimeFormatter.ofPattern方法创建了一个DateTimeFormatter对象,该方法接受一个表示时间格式的字符串参数。在这个例子中,我们使用的时间格式是"yyyy-MM-dd HH:mm:ss",它表示年-月-日 时:分:秒的格式。最后使用LocalDateTime.format方法和创建的DateTimeFormatter对象即可格式化日期和时间。
现在更推荐使用LocalDateTime来表示时间,因为相比Date,LocalDateTime能够提供更多操作时间的方法,与此同时也能保证线程安全。

Date和LocalDateTime的区别

1.LocalDateTime是Java 8引入的新日期和时间API(java.time)的一部分,而Date是Java旧版提供的日期和时间类。LocalDateTime是一个不可变的日期时间对象,它不包含时区信息,而Date类默认表示的是格林威治标准时间(GMT/UTC)。这意味着LocalDateTime更适合用于表示本地日期和时间,而Date则具有全球统一的时间标准
2.LocalDateTime的精度可以达到纳秒级别,而Date的精度仅限于毫秒级别。这使得LocalDateTime在需要高精度时间戳的场景下更为适用。
3.LocalDateTime提供了一系列方法来操作日期和时间,例如加减天数、小时等,还可以进行格式化和解析。这些方法使得LocalDateTime在处理日期和时间时更加灵活和强大。相比之下,Date类的一些方法已经过时,不推荐使用。
4.由于LocalDateTime是不可变的,因此它是线程安全的。这意味着在多线程环境中使用LocalDateTime时,不需要额外的同步措施。而Date可以通过set方法改变,因此在多线程环境中使用时需要谨慎处理线程安全问题

SimpleDateFormat为什么线程不安全?

在java中,SimpleDateFormat类是继承自DateFormat类,在DateFormat类中维护了一个全局的Calendar变量来格式化和解析日期时间。因为在进行格式化时间时,会先后调用Calendar类中的**clear()set()**方法,也就是先清除cal对象中设置的值,再重新设置新的值,由于Calendar内部并没有线程安全机制,并且这两个操作也都不是原子性的,所以当多个线程同时操作一个SimpleDateFormat时就会引起calendar的值混乱。
总结一下,就是SimpleDateFormat类中的Calendar对象可以被多线程共享,而Calendar对象本身不支持线程安全。

如何保证SimpleDateFormat的线程安全?

为了保证SimpleDateFormat的线程安全,可以有以下几种思路:
1.将SimpleDateFormat类对象定义成局部变量:为每个需要格式化或解析的线程创建一个新的SimpleDateFormat对象。这样可以确保每个线程都有自己的数据,不会受到其他线程的干扰。

// 在每个线程中  
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
String formattedDate = sdf.format(new Date());

2.使用ThreadLocal< SimpleDateFormat >:使用ThreadLocal来存储SimpleDateFormat的实例,这样每个线程都将拥有自己的本地变量副本,在线程需要调用时,使用ThreadLocal.get()方法即可获取对应线程中的SimpleDateFormat对象,从而避免了多线程间的共享状态。(推荐!!!)

private static final ThreadLocal<SimpleDateFormat> formatterThreadLocal = ThreadLocal.withInitial(() -> {  
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
});  
// 在需要格式化日期的地方  
SimpleDateFormat sdf = formatterThreadLocal.get();  
String formattedDate = sdf.format(new Date());

3.同步访问SimpleDateFormat:
通过synchronized关键字或lock加锁,从而通过在访问它时进行同步来确保线程安全。但这会降低并发性能,因为它会阻止多个线程同时访问。

4.使用Java 8的DateTimeFormatter:
Java 8引入了新的日期和时间API,其中的DateTimeFormatter是线程安全的。所以可以使用LocalDateTime来表示时间,用DateTimeFormatter来格式化时间,从而解决线程安全问题。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");  
LocalDateTime now = LocalDateTime.now();  
String formattedDateTime = now.format(formatter);

下一篇传送门点我

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值