您的位置:首页 > 娱乐 > 八卦 > 网页制作成品源代码_企业运营管理案例分析_整站seo_新媒体运营

网页制作成品源代码_企业运营管理案例分析_整站seo_新媒体运营

2025/4/19 13:46:42 来源:https://blog.csdn.net/practice_warm/article/details/143481874  浏览:    关键词:网页制作成品源代码_企业运营管理案例分析_整站seo_新媒体运营
网页制作成品源代码_企业运营管理案例分析_整站seo_新媒体运营

Java 面试题

1、Java都有哪些特点?

  1. 简单易学,容易学习和理解。
  2. 面向对象,支持封装,继承,多态等面向对象的特点
  3. 跨平台性,可以做到一段代码,在不同的平台上运行,只要对应的平台安装 Java虚拟机
  4. 内存自动回收机制,做到不需要手动释放内存,Java 使用垃圾回收器管理内存,减少了内存泄漏等情况
  5. 高性能,通过 JIT 即时编译器,将热点的字节码转换为机器码,提高了代码执行的效率
  6. 具有丰富的 AIP 社区,很多开源的 AIP 可以使用

2、面向对象和面向过程的区别?

  1. 面向对象是以对象为基础的编程方式,将数据和函数封装到一起,形成一个对象的实体,面向对象强调的是对象与对象之间的消息通讯,通过定义类和创建对象实现程序的设计和开发。面向对象编程适合解决复杂的问题,将抽象的问题用具体的对象的调用来解决。利用对象解决问题,可以更好的组织和管理代码,提高代码的复用性和可维护性。
  2. 面向过程是以函数为基础的编程方式,将问题拆分为一定的步骤和函数的调用,通过定义函数和全局变量等来实现设计和开发,面向过程更直观,用于简单问题和小型的项目。

3、八种基本数据类型和对应的大小,以及他们的封装类?

  1. 整型:
    1. byte: 1 字节 Byte
    2. shout: 2 字节 Shout
    3. int: 4 字节 Integer
    4. long 8 字节 Long
  2. 浮点型:
    1. float: 4 字节 Float
    2. double: 8 字节 Double
  3. 字符型:
    1. char: 2 字节 Character
  4. 布尔型
    1. boolean: - Boolean (单独:4 字节,在数组中:1 字节)

4、标识符和命名规则?

  • Java 中的标识符使用来标识变量名、方法名、类名、包名等
    • 只能由数字、字母、下划线、美元符号组成
    • 不能以数字开头
    • 标识符严格区分大小写
    • 标识符不能是关键字(classpublic)或保留字(goto)或特殊的值(truefalse
    • 标识符要有具体的描述,要做到见名知意

5、重载和重写的区别?

  • 重载是指在同一个作用域下定义了多个同名的方法,但这些方法参数的种类、数量、顺序不同,编译器可以区分这些方法,重载方法可以提高代码的可读性和灵活性。(两同:同一个类,方法名相同)
  • 重写是重新定义了从父类中继承而来的方法,实现了多态性,即,父类的引用指向子类的对象,调用父类的方法时,执行的是子类中重写的方法。

6、equals与==的区别?

  • equal() 用于比较对象的内容是否相等,在对象没有重写的情况下,默认比较的是地址,只有重写了equal() 方法才会比较内容是否相同。
  • == 对于基本数据类型是比较值,对于引用数据类型是比较对象的地址是否相同,如果两个对象指向同一个内存地址,那么就认为这两个对象相同。

7、Hashcode的作用?

  • hashCode是对象的一个方法,用于计算对象在hash表中的位置
  • 在集合的查找和存储中,hashCode用于快速查询和储存对象,在使用hashMap等集合类时就可以快速的确定存储位置和查找位置,提高代码的运行效率。
  • 对象的判断,两个对象相同,那这两个对象的hash值一定相同,但是,因此可以先比较hash值,如果hash值不相同,这两个对象一定不相同,如果相同,再用equal()判断

8、String、StringBuffer 和 StringBuilder 的区别是什么?

  • String

    • 不可变的,在对String进行操作时,都会创建一个新的对象,对系统的开销很大

    • 线程安全

    • jdk7 时,Spring常量池在永久代,到 jdk8 及以后,Spring常量池被转移到了堆内存中

      • 当使用 Stirng str = “AA”; 时,“AA”会被储存在常量池中,因此,当再次创建“AA”时,不会在创建一个新的字符串,而是引用常量池中存在的字符串。

                String str1 = new String("Hello"); //new出来的都堆中,不会进入常量池String str2 = "Hello";System.out.println(str1 == str2);  //falseString str3 = "Hello";  //进入常量池String str4 = "Hello";  //引用常量池中的数据System.out.println(str3 == str4);  //trueString str5 = new String("Hello");  //进入堆空间,开辟一块空间放入字符串String str6 = new String("Hello");  //进入堆空间,开辟一块空间放入字符串System.out.println(str5 == str6);  //false
        
  • StringBuffer

    • 可变的,可以在原来的基础上进行修改
    • 线程安全的,所有的公开方法都用synchronized
  • StringBuilder

    • 可变的
    • 非线程安全的

9、ArrayList和LinkedList的区别?

  • AllayList
    • 底层使用数组进行储存,当超出数组的范围时,会自动扩容 1.5 倍。
    • 由于用利用数组进行储存,查询效率高,但是删除和插入效率低
  • LinkedList
    • 底层使用双向链表实现的,每个节点包含前一个节点和后一个节点的引用。
    • 由于利用链表进行储存,删除和插入效率高,查询的效率低

10、HashMap和HashTable的区别?

  • hashMap:
    • 底层使用数组+链表+红黑树
    • 线程不安全
    • 允许null键和null值
    • 当hashMap不足时,会进行扩容,默认扩容为原来的 2 倍(因为通过二次hash值计算位置时方便)

11、List,Set,Map三者的区别?

  1. List:
    - List是一个有序的集合,可以包含重复的元素。
    - List允许通过索引访问元素,可以根据元素的位置进行增删改查操作。
    - 常见的List实现类有ArrayList和LinkedList。

  2. Set:
    - Set是一个不允许包含重复元素的集合。
    - Set不保证元素的顺序,即不按照插入顺序或者排序顺序进行存储。
    - Set提供了判断元素是否存在的方法,可以用于去重。
    - 常见的Set实现类有HashSet和TreeSet。

  3. Map:

    • Map是一种键值对的映射表,每个键对应一个值。
      • Map中的键是唯一的,但值可以重复。
      • Map提供了根据键获取值的方法,也可以根据键删除或更新对应的值。
      • 常见的Map实现类有HashMap和TreeMap。

12、Collection包结构,与Collections的区别?

  • Collection是集合类的上级接口。
  • Collections里面封装了有关集合操作的静态方法

13、Java的四种引用,强弱软虚?

  • **强引用:**这是最常见的引用类型,如果一个对象具有强引用,垃圾回收器绝不会回收它,当内存不足时,宁愿抛出错误终止程序,也不会随意

    ​ 回收具有强引用对象来解决内存不足问题。

  • **软引用:**用来描述一些还有用,但是并非必须的对象,将要发生内存溢出异常之前,将会把这些对象列进回收的范围中进行二次回收。

  • **弱引用:**弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,当垃圾回收器工作时,都会回收掉只被弱引用关联的对象

  • **虚引用:**最弱的引用关系,完全不会对其生存时间构成影响,唯一目的就是在这个对象被回收时,收到一个系统通知

14、泛型常用特点?

  • 泛型可以在定义类或接口的时候表示类型,在创建对象的时候指定类型
  • 提高代码的复用性和可维护性。
  • 提高代码的运行效率,泛型在编译时会进行类型擦除,减少运行时的开销,避免了装箱和拆箱的操作

15、Java创建对象有几种方式?

  1. 关键字 new 创建新对象
  2. 通过反射机制
  3. 采用clone机制
  4. 通过序列化机制

16、深拷贝和浅拷贝的区别是什么?

  • 深拷贝,就是创建一个新的对象,新对象将原来对象中的所有信息全部复制下来,如果是引用数据类型,会重新创建对象,然后放入值
  • 浅拷贝,创建一个对象,复制对象的属性,如果属性是引用数据类型,就只是引用原数据,不会创建对象。

17、final有哪些用法?

  • final修饰类,表示该类是最终类,不会被继承,没有子类
  • final修饰方法,表示该方法是最终方法,不能被子类重写
  • final修饰变量,表示该变量是常量,一旦复制,不可被改变
  • final修饰引用数据类型,该数据类型的地址不可变,但是其中的属性可变
  • final修饰参数,表示该方法内,该参数不可变

18、static都有哪些用法?

  1. 静态变量:使用static修饰的变量称为静态变量,也叫类变量。静态变量属于类,而不是属于类的实例。静态变量在类加载时被初始化,并且只有一份拷贝,所有实例共享该变量的值。可以通过类名直接访问静态变量。

    	//1. 静态变量:使用static修饰的变量称为静态变量,也叫类变量。静态变量属于类,而不是属于类的实例。静态变量在类加载时被初始化,并且只有一份拷贝,所有实例共享该变量的值。可以通过类名直接访问静态变量。//2. 静态方法:使用static修饰的方法称为静态方法,也叫类方法。静态方法属于类,而不是属于类的实例。静态方法可以直接通过类名调用,无需创建类的实例。静态方法只能访问静态成员(包括静态变量和静态方法),不能访问非静态成员。//3. 静态代码块:使用static关键字定义的代码块称为静态代码块。静态代码块在类加载时执行,并且只会执行一次。静态代码块常用于初始化静态变量或执行一些只需执行一次的操作。//4. 静态内部类:使用static修饰的内部类称为静态内部类。静态内部类与外部类没有直接的关联,可以直接通过外部类名访问。静态内部类可以拥有自己的静态成员,但不能访问外部类的非静态成员。
    

19、有没有可能两个不相等的对象有相同的hashcode(哈希冲突) ?


20、如何解决哈希冲突?

  • 开放地址法:当发生哈希冲突时,通过一定的探测方法找到下一个可用的位置。常见的探测方法有线性探测、二次探测和双重哈希等。线性探测是指依次往后查找,直到找到一个空闲位置;二次探测是指通过二次方程计算下一个位置;双重哈希是指使用第二个哈希函数来计算下一个位置。

  • 再哈希法:当发生哈希冲突时,使用另一个哈希函数重新计算索引位置。如果仍然发生冲突,可以继续使用其他哈希函数进行再哈希,直到找到一个空闲位置。


21、Object 有哪些常用方法?方法的含义?

  1. equals(Object obj):判断当前对象是否与给定对象相等。默认情况下,equals方法比较的是对象的引用是否相等,即是否指向同一个内存地址。如果需要比较对象的内容是否相等,需要在具体类中重写equals方法。
  2. hashCode():返回当前对象的哈希码值。哈希码是根据对象的内部状态计算得出的一个整数值,用于快速查找和比较对象。在重写equals方时,通常也需要重写hashCode方法。
  3. toString():返回当前对象的字符串表示。默认情况下,toString方法返回的是对象的类名和哈希码值的组合。可以根据需要在具体类中重写toString方法,以便返回更有意义的字符串表示。
  4. getClass():返回当前对象所属的类的Class对象。Class对象包含了关于类的各种信息,可以用于获取类的名称、字段、方法等。
  5. clone():创建并返回当前对象的一个副本。默认情况下,clone方法会创建一个浅拷贝,即只复制对象的引用而不复制对象本身。如果需要实现深拷贝,需要在具体类中重写clone方法。
  6. finalize():在垃圾回收器回收对象之前调用。可以在具体类中重写finalize方法,以便在对象被销毁之前执行一些清理操作。

22、jdk、jre、jvm

  • 理解:

    1. **jvm:**Java虚拟机,是 Java 平台的核心,它使得 Java 程序可以在不同的操作系统上运行而无需编译

    2. **jre:**Java运行时环境提供了Java的核心类库(Java.long / java,util)等,如果如果只是运行Java程序,那jre就能满足

      ​ 需求。

    3. **jdk:**jdk是Java的开发包,包含编译,调试,运行Java代码的开发工具。

  • 关系:jdk 包含 jre ,jre包含jvm

  • jdk = jvm + 核心类库(java.long / java.util) + 开发工具(javac、javadoc、jdb(deBug))


23、Java是半编译、半解释的语言

  • Java 在编译阶段将源代码转换为字节码,在运行阶段又通过解释器或者即时编译器将字节码转换为机器码执行,所以说 Java 是半编译、半解释的语言。

24、注解

  • 注解是一种元数据标记,用于提供关于代码的额外信息,注解不会直接影响程序的执行,但可以被编译器、运行时环境或工具使用
  • 注解通常在类、方法或变量前使用@注解名的形式添加注解。例如,@Override 用于检查方法是否正确重写。
  • 优缺点
    • 优点:增强代码的可读性,提供元数据,支持编译时检查,简化代码编写。
    • 缺点:过度使用注解可能导致代码难以理解,某些注解可能会影响性能。

25、字符型常量和字符串常量

  • 字符型常量是单个字符,用单引号表示;字符串常量是字符序列,用双引号表示。
  • 字符常量用于表示单个字符,如 'A';字符串常量用于表示文本,如 "Hello"
  • 优缺点
    • 优点:字符型常量和字符串常量在 Java 中是基本的数据类型,使用广泛。
    • 缺点:字符串是不可变的,频繁修改字符串内容会导致性能问题

26、标识符和关键字的区别

  • 标识符是用户自定义的名称,关键字是Java预定义的有特殊意义的词。
  • 标识符用于命名类、方法、变量等,关键字用于特定的语法结构,如 iffor 等。
  • 优缺点
    • 优点:标识符提供了灵活性,关键字提供了语言的结构。
    • 缺点:需要避免将关键字作为标识符,否则会导致编译错误。

27、自增(++)和自减(–)

  • 自增和自减是Java中的运算符,用于增加或减少变量的值。
  • ++varvar++ 用于自增,--varvar-- 用于自减。
  • 后置++或- - 是使用后就会生效,即:(i= 2) ——》a = (i + i ++) + (++i) ==> (2 + 2 ++) + (++2) ==> (4) + (++3) ==> 4 + 4 = 8
  • 优缺点
    • 优点:提供了一种简洁的方式来修改变量的值。
    • 缺点:在复杂表达式中使用时可能导致混淆。

28、泛型

  • 泛型是Java中用于支持类型参数化的一种机制,允许在类、接口和方法中使用类型参数。类型擦除是泛型的实现方式,类型擦除的设计是为了兼容那些早期不支持泛型的语言特性,开发者可以在编写代码时享受泛型带来的便利,而编译器则会在编译阶段确保类型安全,然后擦除这些类型信息以适应现有的类结构体系。

29、transient 关键字(阻止序列化)

  • 主要功能
    • transient 关键字用于标记类的字段,使其在对象序列化过程中不被保存。
    • 这可以用于保护敏感数据(如密码)或避免序列化不必要的字段,从而提高性能和安全性。
  • 应用场景
    • 当某些字段不需要或不应该在序列化过程中被保存时,可以使用 transient 关键字。
    • 例如,临时数据、计算结果、敏感信息等。

通过使用 transient 关键字,开发者可以更灵活地控制对象的序列化过程,确保只有必要的数据被持久化。


30、快速失败&安全失败

在Java编程当中,Iterator迭代器是一种用于遍历如List、Set、Map等集合的工具。这类集合部分存在线程安全的问题,例如ArrayList,若在多线程环境下,迭代遍历过程中存在其他线程对这类集合进行修改的话,就可能导致不一致或者修改异常问题,因此,针对这种情况,迭代器提供了两种处理策略:Fail-Fast(快速失败)和Fail-Safe(安全失败)。

先简单介绍下这两种策略——

  1. Fail-Fast(快速失败)机制
    快速失败机制是指集合在迭代遍历过程中,其他多线程或者当前线程对该集合进行增加或者删除元素等操作,当前线程迭代器读取集合时**会立马抛出一个ConcurrentModificationException异常,避免数据不一致。**实现原理是迭代器在创建时,会获取集合的计数变量当作一个标记,迭代过程中,若发现该标记大小与计数变量不一致了,就以为集合做了新增或者删除等操作,就会抛出快速失败的异常。在ArrayList默认启用该机制。

  2. Fail-Safe(安全失败)机制
    安全失败机制是指集合在迭代遍历过程中,若其他多线程或者当前线程对该集合进行修改(增加、删除等元素)操作,当前线程迭代器仍然可以正常继续读取集合遍历,而不会抛出异常。该机制的实现,是通过迭代器在创建时,对集合进行了快照操作,即迭代器遍历的是原集合的数组快照副本,若在这个过程,集合进行修改操作,会将原有的数组内容复制到新数组上,并在新数组上进行修改,修改完成后,再将集合数组的引用指向新数组,,而读取操作仍然是访问旧的快照副本,故而实现读写分离,保证读取操作的线程安全性。在CopyOnWriteArrayList默认启用该机制。


31、Hash的理解

  • hash的基本概念就是把任意长度的输入通过一个hash算法后,映射成固定长度的输出

32、Hash为什么采用高16位异或低16位

  • 优化hash算法,因为散列表的长度一般不会太大,hash值一般就用到低16位(hash & (table.length-1)),高16位没用

    因此,将高16位与低16位进行 异或 操作,让高16位也有用


33、为什么java只有值传递

  • 认识:Java中引用数据类型传递的是该对象的引用副本,在方法中,重新引用不会影响到原有对象,但是,改变对象的属性,可以影响。

  • **安全性和可预测性:**开发人员可以明确的知道方法调用的数据是否被修改,对于基本数据类型,参数的值不会被修

    改,对于引用数据类型,可以清楚的知道,引用所指向的对象的修改会影响到所有指向该对象的引用。这种设计也避免了潜在的错误和混淆,提高程序的安全性。

  • **内存管理一致:**从内存管理的角度来看,值传递机制与 Java 的内存模型相匹配。无论是基本数据类型的值还是引用数据类型的引用,都是在栈内存中进行传递和操作的,而对象本身存储在堆内存中。这种清晰的内存划分有助于提高垃圾回收的效率和准确性,也使得 Java 程序在内存管理方面更加稳定和可靠


34、java常见语法糖

  • 泛型
  • 自动装箱和自动拆箱
  • 增强for循环
  • 可变参数
  • 枚举
  • Lambda 表达式

35、java三大特性

  • 封装性:
    • **定义:**将类的某些信息隐藏在内部,不允许外部直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。
    • **实现方式:**通过四种权限修饰符,public,protected、private、缺省,来控制类、变量和方法的可见性。
    • **优点:**提高代码的安全性,不允许外部随意修改内部数据而导致错误。符合开闭原则(对扩展开放,对修改关闭)
  • 继承:
    • **定义:**继承是子类继承父类的属性和方法,子类可以在不修改父类的情况下,对继承的属性和方法进行扩展和重写。
    • **实现方式:**Java中通过extends关键字实现继承
    • 优点:
      • 提高代码的复用性,子类可以服用父类的代码
      • 类之间的层次更加分明,有利于代码的维护
  • 多态:
    • **定义:**多态是指同一个操作作用于不同的对象可以有不同的表现形式,Java中主要的表现形式为编译时多态(方法重载)和运行时多态(方法重写)
    • **实现方式:**泛型,子类的引用指向父类的对象等。
    • **优点:**提高代码的灵活性和可维护性

36、抽象类和接口的区别

  • 抽象类使用的是abstract关键字定义,而接口使用interface进行定义
  • 抽象类中可以包含抽象方法和普通方法,而接口中的方法默认都是抽象方法,在jdk8之后可以包含默认方法和静态方法(有方法体)
  • 抽象类可以包含各种成员变量,包括实例变量和静态变量,有不同的访问修饰符,而接口中的成员变量默认都是public static final类型的
  • 一个类只能继承一个抽象类,而一个类可以实现多个接口。
  • 抽象类主要用于对事务的抽象,可以提供部分实现,体现的是is a的关系。而接口时定义一种行为规范,它不提供任何实现,体现的是一种can do的关系

37、权限修饰符

  • public:任意位置可见
  • private:本类中可见
  • protected:本包以及子类中可见
  • 缺省:本包中可见

38、@autowired与@Resource

  • @Autowired:先根据类型进行匹配注入,后根据@Qualifier指定的名称进行注入
  • @Resource:先跟据名称进行注入,后根据类型进行匹配

39、Redis基本数据类型

字符串(String)、哈希(hash)、列表(List)、集合(Set)、有序集合(Sorted Set)

40、Redis缓存一致性

  • **设置过期时间:**可以为不同的数据类型固定过期时间,当时间过期时,从数据库中重新加载数据,确保数据的一致性
  • 在数据库更新数据时,先更新数据库,后更新缓存,并为数据更新添加事务,保证数据的一致性
  • 异步队列,将数据的写操作放入消息队列中,由专门的线程获取队列更新数据。

41、Redis缓存雪崩

  • 问题:Redis 缓存雪崩是指在缓存系统中,大量的缓存数据在同一时间失效(过期),或者缓存服务器在短时间内出现大量故障,导致大量的请求直接到达数据库,使得数据库的负载瞬间剧增,甚至可能导致数据库服务器崩溃,从而影响整个系统的性能和可用性。
  • 解决:
    • 设置一定范围的随机的过期时间,如以60分钟为基础,在50 ~ 70 之间的随机失效时间
    • 使用Redis集群,将数据分片储存到多个节点上,当某个节点出现故障时,其他节点继续提供服务保证整体的可用性
    • 主从复制,将数据从主节点复制到多个从节点上,当主节点故障时,从节点升级为主节点,保证服务的连续性

42、为什么String是不可变的

  • 安全性:
    • 防止数据被篡改,当一个String作为参数传递时,如果是可变的,可能在调用者不知情时导致数据的的更改
    • 避免安全漏洞,防止恶意代码篡改敏感信息
  • 缓存优化:
    • 使用字符串常量池保证Stirng的不可变性,节省空间
  • 线程安全:
    • 并发操作的安全性,当多个线程修改同一个字符串时,不会出先一个线程修改,导致其他线程不一致的情况

43、String如何实现不可变的

  • final修饰类
  • 成员变量用private final修饰
  • String类中的所有方法都返回的是一个新的字符串对象,防止对原有的数据的修改

44、反射创建class对象的方法

  • Class.forName("com.example.Person")
  • 使用.class语法
  • 使用getClass()方法

45、异常的分类

编译时异常、运行时异常、错误

46、try - with - resources的异常捕获

当在 try 关键字后面的括号 () 中创建并初始化资源(这些资源需要实现 AutoCloseable 接口)时,在 try 块执行结束后,无论是否抛出异常,这些资源都会被自动关闭。

47、java8新特性

  • Lambda 表达式
  • 函数式接口
  • 方法引用
  • Stream AIP
  • 接口的默认方法和静态方法

48、switch可用的数据类型

  • Java 8 之前,只能用于byteshortintchar,在Java 7开始支持字符串以及对应的包装类
  • 从 Java 14 开始,支持包装类等类型

49、枚举类

枚举类是一种特殊的类,用于表示一组有限的常量

Java 中可以用 enum来定义枚举类

枚举类的表达清晰,提高代码的可读性

50、为什么重写equals和hashcode

  • equals 方法用于判断两个对象是否,相等,如果不重写equals方法,则会比较两个对象的引用,只有指向同一个地址才会返回true

  • hashCode 方法用于为对象生成一个 hash 值,在集合类(HashMap)中,用于快速定位存储对象的位置

    如果不重写,就可能导致相同对象在集合中被重复储存无法从集合中正确的检索到。

51、jvm内存区域有哪些

  • 线程私有区域:

    • 程序计数器,当前线程执行字节码的行号指示器
    • 虚拟机栈,用于存放方法的局部变量表等信息
    • 本地方法栈
  • 线程共享区域:

    • 堆,用于储存对象实体和数组,可以在物理内存上不连续,逻辑内存上连续

    • 方法区,用于储存类信息,常量,静态变量,即时编译器编译后的代码,jdk8后被元空间代替

      元空间不再与堆内存相连避免永久代的内存溢出问题,更好的管理方法区的内存

52、垃圾回收的算法有哪些

  • 标记—清除算法:

    • **过程:**先标记出所有可能需要回收的对象,在标记完成后同一回收所有标记的对象

    • 缺点:

      • **效率问题:**标记和清除两个过程的效率都不高

      • **空间问题:**会产生大量不连续的内存空间碎片,可能导致下一次分配较大的连续内存空间时,无法找到

        足够多的内存而不得不触发另一次的垃圾清除动作

  • 复制算法:

    • **过程:**将可能用到的内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完时,

      将还存活着的对象复制到另一块上面,然后再把使用过的内存空间一次清理掉。

    • **优点:**实现简单,运行效率高,不会产生内存碎片

    • **缺点:**将内存缩小到原来的一半,代价较高

  • 标记—整理算法:

    • 过程:根据对象存活周期的不同将内存划分为几块,一般 Java 把堆内存划分为新生代老年代,在新生代中

      每次垃圾收集时都发现有大批对象死去,少量存活,那就选用复制算法,而老年代存活率高,没有额外空间对它进行分配担保,

      就必须用标记–清理标记–整理算法

53、gc垃圾回收器有哪些

  • Serial 收集器
  • Serial Old 收集器
  • CMS 收集器
  • G1 收集器

54、io与nio的不同之处

  • 面向流与面向缓冲区
    • IO 是面向流的,意味着每次从流中读取一个或多个字节,如果需要再读取已读取的字节,只能重新从新的流中的开始位置读取
    • NIO 是面向缓冲区的,先读取数据到缓冲区,如果需要反复读取可以在缓冲区中前后移动位置进行处理
  • 阻塞与非阻塞
    • IO 流是阻塞的,当一个线程调用read()write()操作时,该线程被阻塞,进入等待状态,无法进行其他任务
    • NIO 流是非阻塞的,当没有数据可读取时,线程不会阻塞,而是做其他的事情,稍后回来检查是否有数据可读取
  • NIO 的选择器
    • 一个选择器可以同时管理多个通道的事件,使得一个线程可以处理大量的连接

55、Java的类加载机制

  • **加载:**在内存中生成一个代表这个类的对象

  • 验证: 确保类的字节码符合JVM的规范和安全要求。

  • **准备:**为类变量(static修饰)分配内存并设置初始值

  • **解析:**将类中的符号引用转化为直接引用

  • **初始化:**执行类的初始化代码

  • Java的类加载机制采用了双亲委派模型,即,当一个类加载器收到类加载请求时,它首先会将请求委派给父类加载器完成

    只有当父类加载器无法完成加载请求时,子类加载器才会尝试自己加载,确保类的类的唯一性安全性防止重复加载和恶意代码的篡改

56、详细说明双亲委派机制

双亲委派机制是 Java 类加载机制中的一种重要设计模式,主要用于确保类的唯一性和安全性。以下是对双亲委派机制的详细说明:

一、定义与工作原理

双亲委派机制规定,当一个类加载器收到类加载请求时,它不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成。如果父类加载器无法加载这个类,子加载器才会尝试自己去加载。这个过程会一直向上委托,直到顶层的启动类加载器。如果启动类加载器也无法加载这个类,再依次向下由子加载器进行尝试加载。

例如,当应用程序类加载器(Application ClassLoader)收到一个加载类 com.example.MyClass 的请求时,它不会立即去加载这个类,而是把请求委派给它的父类加载器——扩展类加载器(Extension ClassLoader)。如果扩展类加载器也无法加载这个类,再继续向上委托给启动类加载器(Bootstrap ClassLoader)。如果启动类加载器仍然无法加载这个类,才会依次向下由扩展类加载器和应用程序类加载器尝试加载。

二、作用

  1. 确保类的唯一性:
  • 通过双亲委派机制,同一个类在 JVM 中只会被加载一次。因为类的加载请求会一直向上委托,最终由顶层的启动类加载器首先尝试加载。如果启动类加载器能够加载这个类,那么就会使用这个加载器加载的类,保证了类的唯一性。
  • 例如,在不同的应用程序中可能都有一个名为 java.util.ArrayList 的类,如果没有双亲委派机制,每个应用程序可能会使用自己的类加载器加载这个类,导致出现多个不同版本的 ArrayList 类,这会引起程序的混乱。
  1. 保证安全性:
  • 双亲委派机制可以防止恶意代码替换核心类库中的类。由于启动类加载器通常是由 JVM 自身实现的,它加载的是核心类库中的类,这些类具有较高的安全性和稳定性。
  • 如果没有双亲委派机制,恶意代码可以通过自定义的类加载器加载一个与核心类库中同名的类,并替换其中的方法,从而对系统造成安全威胁。有了双亲委派机制,当加载核心类库中的类时,会优先由启动类加载器进行加载,确保了核心类库的安全性。

三、实现方式

在 Java 中,双亲委派机制主要是通过 ClassLoader 类的 loadClass() 方法实现的。这个方法的默认实现会首先检查父类加载器是否已经加载了这个类,如果没有,则调用父类加载器的 loadClass() 方法进行加载。如果父类加载器无法加载这个类,才会调用当前类加载器的 findClass() 方法尝试自己去加载。

以下是一个简单的示例代码,展示了类加载器的层次结构和双亲委派机制的实现:

public class ClassLoaderExample {public static void main(String[] args) throws ClassNotFoundException {// 获取应用程序类加载器ClassLoader appClassLoader = ClassLoaderExample.class.getClassLoader();System.out.println("Application ClassLoader: " + appClassLoader);    // 获取扩展类加载器ClassLoader extClassLoader = appClassLoader.getParent();System.out.println("Extension ClassLoader: " + extClassLoader);// 获取启动类加载器ClassLoader bootstrapClassLoader = extClassLoader.getParent();System.out.println("Bootstrap ClassLoader: " + bootstrapClassLoader);// 加载一个类,触发双亲委派机制Class<?> clazz = Class.forName("java.lang.String");System.out.println("Loaded class: " + clazz.getClassLoader());}
}

在这个示例中,我们首先获取了应用程序类加载器、扩展类加载器和启动类加载器,并打印它们的信息。然后,我们使用 Class.forName() 方法加载 java.lang.String 类,这个过程会触发双亲委派机制,最终由启动类加载器加载这个类。

总之,双亲委派机制是 Java 类加载机制中的一个重要设计模式,它确保了类的唯一性和安全性,是 Java 语言安全稳定运行的重要保障。

57、tomcat是如何打破双亲委派机制的

  • 打破双亲委派机制的方法是通过重写类加载方法,先通过自己加载,如果加载不了,在委派给父类加载
  • Tomcat 通过重写类加载方法和打破双亲委派机制,实现了在一个服务器中部署多个 Web 应用程序时,各个应用程序之间类的隔离、独立加载、热部署和热加载等功能。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com