一.static和final的用法
1.static
- 修饰变量:被修饰的变量称为静态变量或类变量,它属于类而不是实例,所有实例共享同一个静态变量。
- 修饰方法:被修饰的方法称为静态方法,它属于类而不是实例,直接通过类名调用,无需创建对象
- 修饰代码块: 被修饰的代码块称为静态代码块,它在类加载时执行,且只执行一次
自我理解:被修饰的变量和方法在运行时,存储在方法区和静态区,是程序运行时被分配的一块内存空间。实例是指类创建的具体对象,无论有多少的对象,静态变量只有一份,当一个实例修改了变量,其他实例也会受到影响。生命周期同类生命周期一致,类加载时初始化,程序结束或类被卸载时销毁。
2.final
- 修饰类:不能被继承
- 修饰方法:不能被子类重写
- 修饰变量:一旦赋值后不可改变
使用 static
和 final
的一些常见场景包括:
- 使用static可以创建工具类,其中方法直接通过类名调用
- 使用final可以定义常量,保证其值不被修改
- 使用static final可以定义全局变量
二.抽象类与接口的区别
1.定义方式:
- 抽象类使用abstract关键字定义,可以包含抽象方法和具体方法,也可以包含成员变量;
- 接口使用interface关键字定义只能包含抽象方法和常量,不能包含成员变量
- 注:在Java8之后,接口中增加一些新特性:默认方法,静态方法,私有方法
- 自我理解: 增加新特性后最大的便利在于接口中定义的方法,不在只是抽象方法了,不是必须重写,对开发者来说,在对接口修改时,其实现类无需修改,更加灵活。
2.继承关系:
- 一个类只能继承一个抽象类,但可以实现多个接口,抽象类通过继承来实现代码的复用,而接口通过实现来实现代码的复用。
3.实现方式:
- 子类继承抽象类时需要使用extends关键字;实现接口时,需要使用implements关键字
4.设计目的:
- 抽象类主要表示类的共性,它是一种模板用于派生具体子类;接口用于定义一组相关的操作,类似于功能的作用
三.类有哪些基本特性?优点是什么?(或者面向对象的三大特性)
具有封装性,继承性,多态性
- 封装性:类的封装性为类的成员提供共有、缺省、私有、保护等访问权限,目的是隐藏类中的私有变量和类中方法的实现细节
- 继承性;允许通过继承原有类的某些特性或全部特性而产生全新的类,原有的类成为父类,新创建的类成为子类,子类可以继承父类的共性,也可以创建出它特有的个性。
- 多态性:是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型和表现出来不同的行为,表现形式:重载和重写
四.重载和重写的区别
- 重载:指在同一类中定义多个方法名相同的方法,参数类型和个数不同,返回值必须相同
- 重写:指在子类中重新定义父类中的方法,方法名、参数列表和返回类型必须相同
- 自我理解:重载在同一个类中,重写在父子类中,重载同名方法,不同参数,同返回值,重写子类需要与父类中的定义的方法---"全部相同"。
五.Error和Exception有什么区别?
共同点:
- 都是Throwable的子类
区别:
- Error:为系统错误,不可恢复,例如:内存溢出,栈溢出等
- Exception: 程序中的异常,通常为逻辑错误,外部环境等原因引起,分为以下两种类型:
- 检查异常: 必须处理或者捕获,在方法的声明中使用throws关键字声明或使用try-catch语句进行捕获和处理。例如,IOException、SQLException等都是受检异常
- 非检查异常:通常由程序逻辑错误引起,可以选择捕获和处理,也可以不处理,例如NullPointerException、ArrayIndexOutOfBoundsException等。
- 自我理解:Error不能恢复的系统异常,Exception中,非检查异常由于代码逻辑和代码编辑错误,及大意引起开的程序异常,这些错误是不应该发生的,应尽量避免。检查异常,编写某些代码需要必须处理的异常,可以交给异常处理器统一处理。在编写一些业务上的逻辑问题时,我们也可以去主动捕获异常,如文本格式要求,获取参数的格式要求等,不符合业务要求我们可以统一交给异常处理器,返回给前端进行显示,提醒用户重新编辑。
六.Java会存在内存泄漏吗?
会的,内存泄漏指程序中的内存被分配后,无法回收和释放,导致内存不断增加,最终可能导致内存耗尽。
什么情况下,会出现内存泄漏:
- 对象引用为及时释放:例如,长时间持有对象引用的静态变量、集合中的对象引用未及时移除等情况都可能导致内存泄露
- 资源未被正确释放:例如,忘记关闭文件流、数据库连接等情况都可能导致内存泄露。
- 循环引用:例如,两个对象相互引用,但没有其他对象引用它们,就会导致内存泄露。
七.多线程有几种实现方法,都是什么?同步有几种实现方法都是什么?
多线程的实现方法有三种:
1. 继承Thread类:创建一个继承自Thread类的子类,并重写run()方法来定义线程的执行逻辑。然后通过创建子类的实例并调用start()方法来启动线程。
2. 实现Runnable接口:创建一个实现了Runnable接口的类,并实现其run()方法来定义线程的执行逻辑。然后通过创建该类的实例,并将其作为参数传递给Thread类的构造方法来创建线程对象,最后调用线程对象的start()方法来启动线程。
3. 使用线程池:通过使用Java提供的Executor框架,可以创建一个线程池来管理线程的执行。可以使用ThreadPoolExecutor类来创建线程池,并通过submit()方法提交任务给线程池执行。
同步的实现方法有四种:
1. synchronized关键字:使用synchronized关键字可以修饰方法或代码块,实现对共享资源的同步访问。当一个线程获取到对象的锁时,其他线程将被阻塞,直到该线程释放锁。
2. ReentrantLock类:ReentrantLock是Java提供的一个可重入锁,通过lock()方法获取锁,通过unlock()方法释放锁。与synchronized关键字相比,ReentrantLock提供了更多的灵活性和功能,例如可中断锁、公平锁等。
3. ReadWriteLock接口:ReadWriteLock是Java提供的一个读写锁接口,通过它可以实现对共享资源的读写操作的并发访问。它提供了读锁和写锁两种锁的机制,读锁可以被多个线程同时获取,写锁只能被一个线程获取。
4. AtomicInteger类:AtomicInteger是Java提供的一个原子操作类,它可以实现对整型变量的原子操作。通过使用AtomicInteger可以避免多线程环境下的竞态条件和数据不一致的问题。
八.sleep()和wait()的区别
- 功能:
sleep()函数用于暂停当前线程的执行一段时间
wait()函数用于等待子线程结束
- 使用场景:
sleep()函数通常用于在程序中添加延迟或等待一段时间后执行某些操作
wait()函数通常在父线程中使用,用于等待子线程完成执行后在继续执行父进程的代码
- 阻塞:
sleep()函数会阻塞当前线程的执行,即暂停当前线程的运行,但不会影响其他线程的执行
wait()函数会阻塞当前线程的执行,即暂停当前进程的运行,直到指定的子线程结束
- 返回值
sleep()函数没有返回值
wait()函数会返回子线程的退出状态
- 如果返回值大于0,表示返回的是子进程的PID,子进程已经退出。
- 如果返回值等于0,表示返回的是子进程的PID,子进程正在运行。
- 如果返回值小于0,表示发生了错误,返回的是一个负数,表示等待子进程的过程中出现了错误。
- 参数
sleep()函数接受一个参数,表示暂停的时间长度(单位为秒)
wait()函数可以通过参数来指定等待的子进程
九.Java跨平台原理
java源程序(.java文件)通过编译器编译成为Class文件(字节码文件),而它的class文件是基于字节码(以byte为单位存储的文件),而字节码文件描述的是程序要运行的虚指令(可以理解为一种机器指令)的集合,这种虚指令于平台无关,java虚拟机可以识别它(不同安装响应的jre,运行jvm,就可以)
十.有了基本类型,为什么还需要需要包装类型
Java是一种面向对象的编程语言,基本类型并不具备对象的特征,为了让基本类型就有对象的特征,就出现了包装类。它相当于将基本类型包装起来,使得它具有了对象的特性。另外当使用ArrayList,HashMap中放东西时,因为容器都是装的Object的,这时候就需要包装类型了。
十一."=="和"equals"的区别
- ==比较两个对象的内存地址值,相同返回true,不同false
- equals默认情况下时于==一样的,但是重写了equals()方法后,通常比较的内容是否相同
十二.讲一下java中的集合
集合(Collection)是一种存储和操作一组对象的容器,Java提供了多种集合类,每个集合类都有不同的特点和用途。
1.List(列表):可以包含重复元素
- ArrayList:基于数组实现的动态数组,支持随机访问和快速插入/删除元素
- LinkedList: 基于链表实现的双向链表,支持快速插入/删除元素,但访问元素需要遍历链表
- Vector:与ArrayList类似,但是是线程安全的,支持同步访问
2.Set(集):是无序的集合,不允许包含重复的元素。
- HashSet: 基于哈希表实现的集合,不保证元素的顺序,查找速度快
- LInkedHashSet: 基于Hash表和链表实现的集合,按照插入顺序维护元素的顺序
- TreeSet: 基于红黑树实现的集合,按照元素的自然顺序或者指定的比较器进行排序
3.Map(映射):是一种键值对的集合,每一个建只能对应一个值
- HashMap: 基于哈希表实现的映射,不保证键值对的顺序,查找速度快
- LinkedHashMap: 基于哈希表和链表实现的映射,按照插入顺序维护键值对的顺序
- TreeMap: 基于红黑树实现的Map,按照键的自然顺序或者指定的比较器进行排序
十三.String、StringBuffer、StringBuilder的区别?
- 运行速度:StringBuild>StringBuffer>String
- 不可变性:String是不可变的,每回创建或修改,都会创建一个新的对象。StringBuild和StingBuffer是可变的,对其修改不会创建新的对象。
- 线程安全:StringBuilder是线程不安全的,而StringBuffer是线程安全的。
- String:适用于少量的字符串操作的情况
- StringBuilder: 适用于单线程下在字符缓冲区进行大量操作的情况
- StringBuffer: 适用于线程下在字符缓冲区进行大量操作的情况
十四. 静态变量和实例变量的区别?
- 存储位置:静态变量存储在静态存储区,实例变量存储在堆内存中的对象中
- 内存分配:静态变量在程序启动的时候就会分配内存,在程序的整个运行期间都存在。实例变量在创建对象的时候才会分配内存,每个对象都有自己的实例变量
- 生命周期:静态变量的生命周期同程序的生命周期相同,而实例变量的生命周期与对象的生命周期相同
- 访问方式:静态变量通过类名直接访问,也可以通过对象引用访问。实例变量只能通过对象引用访问。
- 初始化时机:静态变量在类加载的时候被初始化,实例变量在创建对象的时候初始化。
十五.同步异步区别,什么情况下使用?
- 同步:指任务顺序执行,每个任务执行完在执行下一个任务。在同步执行中,任务之间是相互依赖的,后面的任务必须等前面的任务执行完毕才能执行,同步执行简单直观,可能导致程序执行效率低。
- 异步:指任务的执行不按顺序执行,每个任务在执行过程中可以同时执行其他任务,任务之间是相互独立的,不需要等待其他任务的完成。可以提高程序的执行效率。
执行时间较短的任务可以选择同步执行;较快的可以选择异步执行;并发性要求较高的任务选择异步执行,提高并发和响应;任务之间存在依赖关系的可以选择同步执行。
十六.Java中有几种类型的流?
- 字节流:字节流以字节为单位进行读取和写入数据。设用于处理二进制数据或字节流形式的文本。主要由:InputStream和OutputStream及其子类组成。InputStream:用于从输入源读取字节数据的抽象类。OutputStream:用于向输出目标写入字节数据的抽象类。
- 字符流(Character Stream):字符流以字符为单位进行读取和写入数据。它们适用于处理文本数据,可以自动处理字符编码和解码。在Java中,字符流主要由Reader和Writer类及其子类组成。Reader:用于从输入源读取字符数据的抽象类。Writer:用于向输出目标写入字符数据的抽象类。
十七.数组中没有有length()这个方法?String有没有length()这个方法?
数组中没有length()方法,但是数组中有length这个属性。用来标识数组的长度。String中有这个方法用来得到字符串的长度。
十八.super与this的区别
-
super
关键字:super
用于在子类中访问和调用父类的成员(属性和方法)。- 可以使用
super
关键字来调用父类的构造函数,以便在子类中初始化父类的属性。 - 在子类中,可以使用
super
关键字来调用父类的方法,以便在子类中扩展或重写父类的方法。
-
this
关键字:this
用于在类的内部访问和引用当前对象的成员(属性和方法)。this
关键字可以用于访问当前对象的属性和方法,以及调用当前对象的构造函数。- 在类的方法中,可以使用
this
关键字来引用当前对象,以便在方法内部访问和操作当前对象的成员。
十九.子类继承父类,子类和父类中都有静态常量,静态代码块,构造方法,那么执行顺序是怎么样的?
- 执行顺序如下:
- 首先,父类的静态代码块会在子类加载之前执行
- 接下来子类静态代码块会在子类加载时执行
- 父类的静态常量会在父类的静态代码块之后进行初始化
- 父类的构造方法会在子类的构造方法之前执行
- 子类的静态常量会在子类的静态代码块之后进行初始化
- 子类的构造方法会执行
- 总结:父类的静态代码块→子类的静态代码块→父类的静态常量→父类的构造方法→子类的静态常量→子类的构造方法
二十.Java中的类加载器?
建议二十和二十一一起看!!!
- 启动类加载器(Bootstrap Class Loader):它是JVM的一部分,负责加载java的核心类库,如java.long包中的类,它是最顶级的类加载器,由C++实现。
- 扩展类加载器(Extension Class Loader):它是由sun.misc.Launcher$ExtClassLoader实现的,负责加载Java的扩展类库,如javax包中的类。它是由Java编写的类加载器。
- 应用程序类加载器(Application Class Loader):它是由sun.misc.Launcher$AppClassLoader实现的,负责加载应用程序的类。它是Java中默认的类加载器,也是大多数开发者使用的类加载器。
二十一.双亲委派
好处:
- 主要为为了安全性,避免用户自己编写的类动态替换java中的一些核心类,
- 同时避免类的重复加载,因为JVM区分不同的类。不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类
二十二.转发和重定向的区别?
-
转发(Forward):转发是指在服务器内部将请求从一个资源(如Servlet、JSP)传递给另一个资源进行处理。在转发过程中,客户端浏览器并不知道服务器内部的转发操作,它只知道最终响应的资源。转发是通过请求的转发器(RequestDispatcher)实现的。
特点:
- 转发是服务器内部的操作,客户端浏览器并不知道转发的过程。
- 转发是在同一个请求中完成的,浏览器地址栏不会改变。
- 转发可以共享请求的属性和参数。
- 转发可以将请求转发给同一个Web应用程序中的其他资源。
使用场景:
- 在同一个Web应用程序中的不同资源之间进行数据共享和处理。
- 实现MVC架构中的控制器转发请求给视图进行渲染。
-
重定向(Redirect):重定向是指服务器返回一个特殊的响应给客户端浏览器,告诉它重新发送一个新的请求到指定的URL。客户端浏览器会根据重定向响应的URL重新发送请求。重定向是通过设置响应的状态码和Location头实现的。
特点:
- 重定向是通过向客户端浏览器发送特殊的响应来实现的。
- 重定向会导致浏览器地址栏的URL发生改变。
- 重定向不共享请求的属性和参数。
- 重定向可以将请求重定向到任意URL,包括其他Web应用程序。
使用场景:
- 在不同的Web应用程序之间进行页面跳转。
- 处理表单提交后的重定向,避免表单的重复提交。
总结: 转发和重定向都是用于实现请求的转发或跳转,但它们的实现方式和特点有所不同。转发是服务器内部的操作,不会改变浏览器地址栏的URL,可以共享请求的属性和参数;而重定向是通过向客户端浏览器发送特殊的响应来实现的,会改变浏览器地址栏的URL,不共享请求的属性和参数。
类比下:
转发:张三(客户端)去找李四(服务器)借钱,李四没有钱,去找了王五借钱,借到钱后借给了张三,这个过程中张三是不知道李四去借钱的。
重定向:张三(客户端)去找李四(服务器)借钱,李四告诉张三我没钱,去找王五借钱吧,张三就去找王五借钱了。
二十三.Integer a1 = 100; Integer a2 = 100; a1 == a2 结果是什么,并解释?
true,因为在Integer中包含一个享元模式,会存储一个[-128,127]的缓存,当a1=100,a2=100这量条命令时触发装箱,会取缓存,最终指向的是同一个内存地址,结果为true。但是假如a2=200的话,不在缓存的取值范围内,就会创建一个新的对象,a1和a2在进行==对比时地址值就不相同了,结果为false。