java面试笔记

JAVA基础

1.1 Java语言有哪些特点

1、简单易学、有丰富的类库
2、面向对象(Java最重要的特性,让程序耦合度更低,内聚性更高)
3、与平台无关性(JVM是Java跨平台使用的根本
4、可靠安全
5、支持多线程

1.2 面向对象和面向过程的区别

面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,在使用的时候一一调用则可。性能较高,所以单片机、嵌入式开发等一般采用面向过程开发
面向对象:是把构成问题的事务分解成各个对象,而建立对象为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要低。

1.3 八种基本数据类型的大小,以及他们的封装类

基本类型大小(字节)默认值封装类
byte1(byte)0Byte
short2(short)0Short
int40Integer
long80LLong
float40.0fFloat
double80.0dDouble
boolean-falseBoolean
char2\u0000(null)Character

1.4 instanceof 关键字的作用

instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:

boolean result = obj instanceof Class
其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型
System.out.println(i instanceof Object);//编译不通过

Integer integer = new Integer(1);
System.out.println(integer instanceof  Integer);//true

//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回false。
System.out.println(null instanceof Object);


1.5 Java自动装箱与拆箱

装箱就是自动将基本数据类型转换为包装器类型(int–>Integer);调用方法:IntegervalueOf(int) 方法
拆箱就是自动将包装器类型转换为基本数据类型(Integer–>int)。调用方法:IntegerintValue方法

但在JAVA中Integer对象,有一个缓存机制 IntegerCache

Java中的IntegerCache是一个内部机制,用于缓存一定范围内的整数对象,以提高性能和节省内存。这个缓存主要用于Integer类,但对于一些其他整数包装类,如Byte、Short、Long等,也有类似的缓存。

下面是关于IntegerCache的一些重要信息:

整数范围缓存: Java缓存了一个默认范围内的整数对象,默认情况下是从-128到127。这意味着在这个范围内创建的整数对象都将重用缓存中的现有对象,而不是创建新的对象。这可以减少内存消耗并提高性能,因为相同的整数值不会在内存中重复存在。

1.6 重载和重写的区别

重写(Override)

其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。

总结

1.发生在父类与子类之间
2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
3.访问修饰符的限制一定要大于被重写方法的访问修饰符`(public>protected>default>private)`
4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常

重载(Overload)

在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载。

1.重载`Overload`是一个类中多态性的一种表现
2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准

1.7 equals与==的区别

== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
1、比较的是操作符两端的操作数是否是同一个对象
2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如:
int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。

equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断。

1.8 Hashcode的作用

java的集合有两类,一类是List,还有一类是Set前者有序可重复,后者无序不重复

当我们在set中插入的时候怎么判断是否已经存在该元素呢,可以通过哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域。

hashCode方法可以理解:它是根据对象的内存地址换算返回出的一个值。当集合要添加新的元素时,先调用这个元素的hashCode方法,就能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

1.9 String、StringBufferStringBuilder 的区别是什么?

String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象。

private final char value[];

每次+操作 : 隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法 拼接+后面的字符。
StringBufferStringBuilder他们两都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到

/**
  * The value is used for character storage.
  */
  char[] value;

他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBufferStringBuilder来进行操作。 另外StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的

1.10 ArrayListlinkedList的区别

Array(数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。
Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大,因为这需要重排数组中的所有数据,(因为删除数据以后, 需要把后面所有的数据前移)
缺点: 数组初始化必须指定初始化的长度, 否则报错

List 是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承Collection
List 有两个重要的实现类:ArrayListLinkedList

LinkedList 在指数据量很大或者操作很频繁比 ArrayList 更适合使用。因为LinkedList 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在getset方面弱于ArrayList

ArrayList: 可以看作是能够自动增长容量的数组
ArrayListtoArray方法返回一个数组
ArrayList asList方法返回一个列表
ArrayList 底层的实现是Array, 数组扩容实现

1.11 HashMap和HashTable的区别

1、两者父类不同
HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。

2、对外提供的接口不同
HashtableHashMap多提供了elments() 和contains() 两个方法。
elments() 方法继承自Hashtable的父类Dictionnaryelements() 方法用于返回此Hashtable中的value的枚举。
contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,contansValue() 就只是调用了一下contains() 方法。

3、对null的支持不同
Hashtablekeyvalue都不能为null
HashMapkey可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的valuenull

4、安全性不同
HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。
Hashtable是线程安全的,它的每个方法上都有synchronized 关键字,因此可直接用于多线程中。
虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,这样设计是合理的,因为大部分的使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap
ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。

5、初始容量大小和每次扩充容量大小不同

6、计算hash值的方法不同

HashMap的底层原理

​ HashMap的底层数据结构是一个数组,数组中的每个元素称为桶(bucket),每个桶实际上是一个链表。当新元素插入到HashMap中时,首先根据键的哈希码来计算出桶的位置,然后将该元素添加到该桶所对应的链表中。

​ 当我们在HashMap中查找元素时,它首先使用键的哈希码来计算出元素所在的桶的位置,然后遍历该桶对应的链表,查找键所对应的值。由于每个桶可能包含多个元素,因此查找的时间复杂度通常为O(1)。

​ 需要注意的是,在添加元素时,如果哈希码相同但键不同,则会在同一桶中创建一个新的链表节点,这被称为哈希冲突。为了减少哈希冲突的数量,HashMap在每个桶上设置了一个阈值,当链表长度超过阈值时,会将链表转换为红黑树,以提高查找效率。

​ 另外,为了保持HashMap的性能,当桶的数量达到一定程度时,HashMap会自动扩容。在扩容过程中,HashMap会重新计算每个元素在新数组中的位置,这个过程是比较耗时的,因此需要在实际使用时留足够的空间以减少扩容的频率。

红黑树的概念

​ 红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它在每个节点上添加了一个额外的信息来确保树保持平衡。红黑树的名称来源于这些额外信息,每个节点都有一个颜色属性,可以是红色或黑色。红黑树的设计旨在满足以下几个关键性质:

  1. 节点颜色属性:每个节点要么是红色,要么是黑色。
  2. 根节点是黑色的:树的根节点必须是黑色的。
  3. 叶子节点为黑色:叶子节点(NIL节点,也称为空节点)都被视为黑色。这些节点通常表示树的末端或空节点。
  4. 红色节点规则:如果一个节点是红色的,则其子节点必须是黑色的。也就是说,不能有两个相邻的红色节点。
  5. 黑高度平衡:从根节点到任何叶子节点的路径上的黑色节点数必须相等。这个性质确保了树的平衡,因为最长路径不会超过最短路径的两倍。

这些性质保证了红黑树在插入和删除节点时能够自我平衡,使得树的高度保持在对数级别,从而保持了基本操作(查找、插入、删除)的时间复杂度在最坏情况下也是对数时间。

红黑树常用于实现有序集合和关联数组等数据结构,因为它们提供了高效的插入、删除和查找操作。在许多编程语言的标准库中,都有基于红黑树的实现,例如C++的std::mapstd::set,以及Java的TreeMapTreeSet

虽然红黑树的维护在理论上较为复杂,但由于其严格遵循上述性质,可以使用一套相对简单的规则来确保平衡。这使得红黑树成为一种强大而高效的自平衡二叉搜索树。

1.12 Collection包结构,与Collections的区别

Collection是集合类的上级接口,子接口有 Set、List、LinkedList、ArrayList、Vector、Stack、Set
Collections是集合类的一个帮助类, 它包含有各种有关集合操作的静态多态方法,用于实现对各种集合的搜索、排序、线程安全化等操作。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

总结:Collection 表示集合接口和包结构,定义了集合的通用操作,而 Collections 是一个工具类,提供了对集合的各种操作方法。前者用于定义集合,后者用于操作和处理集合。

1.13 Java的四种引用,强弱软虚

强引用:
强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:

String str = new String("str");

12

软引用
软引用在程序内存不足时,会被回收,使用方式:

// 注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的,
// 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));
123

可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象。

弱引用
弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:

WeakReference<String> wrf = new WeakReference<String>(str);
1

可用场景: Java源码中的 java.util.WeakHashMap 中的 key 就是使用弱引用,我的理解就是,一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作。

虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入ReferenceQueue 中。注意哦,其它引用是被JVM回收后才被传入ReferenceQueue 中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有 ReferenceQueue ,使用

例子:

PhantomReference<String> prf = new PhantomReference<String>(new String("str"), new ReferenceQueue<>());
1

可用场景: 对象销毁前的一些操作,比如说资源释放等。Object.finalize() 虽然也可以做这类动作,但是这个方式即不安全又低效
上诉所说的几类引用,都是指对象本身的引用,而不是指 Reference 的四个子类的引用( SoftReference 等)。

1.15 泛型常用特点

泛型是Java SE 1.5之后的特性, 《Java 核心技术》中对泛型的定义是:

“泛型” 意味着编写的代码可以被不同类型的对象所重用。

“泛型”,顾名思义,“泛指的类型”。我们提供了泛指的概念,但具体执行的时候却可以有具体的规则来约束,比如我们用的非常多的ArrayList就是个泛型类,ArrayList作为集合可以存放各种元素,如Integer, String,自定义的各种类型等,但在我们使用的时候通过具体的规则来约束,如我们可以约束集合中只存放Integer类型的元素,如

List<Integer> iniData = new ArrayList<>();
1

使用泛型的好处?

以集合来举例,使用泛型的好处是我们不必因为添加元素类型的不同而定义不同类型的集合,如整型集合类,浮点型集合类,字符串集合类,我们可以定义一个集合来存放整型、浮点型,字符串型数据,而这并不是最重要的,因为我们只要把底层存储设置了Object即可,添加的数据全部都可向上转型为Object。 更重要的是我们可以通过规则按照自己的想法控制存储的数据类型。

1.16 Java创建对象有几种方式?

java中常见的创建对象的方式:

  1. 使用 new 关键字

    • 最常见的方式是使用 new 关键字来创建对象,这将调用类的构造方法(默认构造方法或自定义构造方法)来初始化对象。
    MyClass obj = new MyClass(); // 创建 MyClass 类的对象
    
  2. 使用 Class.forName()

    • 你可以使用 Class.forName() 方法根据类的全限定名来动态创建对象。这通常用于创建在编译时不可知的类的实例。
    String className = "com.example.MyClass";
    Class<?> myClass = Class.forName(className);
    Object obj = myClass.newInstance(); // 创建对象
    
    
  3. 使用反射(Reflection)

    • Java 的反射机制允许你通过类的名称、构造方法、字段等信息来创建对象,这在某些情况下非常有用,但需要小心使用,因为它增加了代码的复杂性。
    Class<?> myClass = MyClass.class;
    Constructor<?> constructor = myClass.getConstructor();
    Object obj = constructor.newInstance(); // 创建对象
    
    
  4. 使用对象克隆

    • 一些类实现了 Cloneable 接口,你可以使用 clone() 方法创建对象的副本。需要注意的是,克隆通常是浅克隆,它只复制对象本身,而不复制对象内部引用的其他对象。
    MyClass original = new MyClass();
    MyClass cloned = (MyClass) original.clone(); // 创建克隆对象
    
    
  5. 使用工厂方法

    • 你可以使用工厂方法设计模式来创建对象。工厂方法通常是一个静态方法,用于创建对象并返回它,隐藏了对象的创建细节。
    public class MyClassFactory {
        public static MyClass createInstance() {
            return new MyClass();
        }
    }
    
    MyClass obj = MyClassFactory.createInstance(); // 使用工厂方法创建对象
    
    
  6. 使用单例模式

    • 单例模式确保一个类只有一个实例,并提供一个全局访问点。你可以使用单例模式来获取类的唯一实例。
    public class Singleton {
        private static Singleton instance;
        
        private Singleton() { }
        
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    Singleton obj = Singleton.getInstance(); // 获取单例对象
    
    

1.17 有没有可能两个不相等的对象有相同的hashcode

有可能.在产生hash冲突时,两个不相等的对象就会有相同的 hashcode 值.当hash冲突产生时,一般有以下几种方式来处理:

拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储.

开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入 再哈希:又叫双哈希法,有多个不同的Hash函数.当发生冲突时,使用第二个,第三个….等哈希函数计算地址,直到无冲突.

以下是几种常见的开放定址法探测方法:

  1. 线性探测(Linear Probing):当发生冲突时,线性探测会顺序地检查下一个槽位,直到找到一个空槽位或者查遍整个哈希表。
  2. 二次探测(Quadratic Probing):二次探测使用二次方程来计算下一个探测位置,以便更分散地查找槽位。如果当前位置发生冲突,就尝试下一个位置、下一个位置的平方、再下一个位置的平方,依此类推,直到找到一个空槽位。
  3. 双重散列(Double Hashing):双重散列使用第二个哈希函数来计算下一个探测位置。如果当前位置发生冲突,就使用第二个哈希函数计算下一个位置,然后再次检查,直到找到一个空槽位

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

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.

深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的对象都复制了一遍.

**零拷贝(Zero Copy)**是一种优化技术,用于减少数据传输和复制的开销,特别是在处理大量数据时。零拷贝的目标是最小化数据从一个地方到另一个地方的复制次数,以提高性能和降低内存开销。虽然Java本身提供了一些高效的I/O库和内存管理工具,但直接实现真正的零拷贝通常比较复

以下是一些与零拷贝相关的Java特性和库:

  1. NIO(New I/O):Java的**NIO包提供了一种非阻塞的I/O模型,它可以通过直接缓冲区(Direct ByteBuffer**)来实现零拷贝。Direct ByteBuffer 可以将数据从内存复制到直接缓冲区,然后在网络传输或文件I/O时避免额外的数据复制。
  2. Java Native Interface(JNIJNI允许Java代码与本地C或C++代码进行交互。通过JNI,你可以利用本地代码中的零拷贝技术,将数据从Java中传递给本地代码,然后在本地代码中进行处理。
  3. Java网络编程库:一些Java网络编程库和框架(如Netty)采用了零拷贝技术,以提高网络通信的性能。Netty使用**Direct ByteBuffer**来支持零拷贝传输数据。
  4. 内存映射文件(Memory-Mapped Files):Java提供了内存映射文件的支持,允许将文件内容映射到内存中,然后通过内存操作来读写文件,从而减少了I/O操作中的数据拷贝。

1.19 final有哪些用法?

  1. 被final修饰的类不可以被继承
  2. 被final修饰的方法不可以被重写
  3. 被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.
  4. 被final修饰的方法,**JVM**会尝试将其内联,以提高运行效率
  5. 被final修饰的常量,在编译阶段会存入常量池中.

1.20 Excption与Error包结构

Java可抛出(Throwable)的结构分为三种类型:被检查的异常(CheckedException),运行时异常(RuntimeException),错误(Error)。

1、运行时异常
定义:RuntimeException及其子类都被称为运行时异常。
特点:Java编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。例如,除数为零时产生的ArithmeticException异常,数组越界时产生的IndexOutOfBoundsException异常,fail-fast机制产生的ConcurrentModificationException异常(java.util包下面的所有的集合类都是快速失败的,“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出ConcurrentModificationException 异常,从而产生fail-fast机制,这个错叫并发修改异常。Fail-safe,java.util.concurrent包下面的所有的类都是安全失败的,在遍历过程中,如果已经遍历的数组上的内容变化了,迭代器不会抛出ConcurrentModificationException异常。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是ConcurrentHashMap迭代器弱一致的表现。
ConcurrentHashMap的弱一致性主要是为了提升效率,是一致性与效率之间的一种权衡。要成为强一致性,就得到处使用锁,甚至是全局锁,这就与Hashtable和同步的HashMap一样了。)等,都属于运行时异常。

常见的五种运行时异常:

ClassCastException(类转换异常)
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针异常)
ArrayStoreException(数据存储异常,操作数组是类型不一致)
BufferOverflowException

2、被检查异常
定义:Exception类本身,以及Exception的子类中除了"运行时异常"之外的其它子类都属于被检查异常。
特点 : Java编译器会检查它。 此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。例如,CloneNotSupportedException就属于被检查异常。当通过clone()接口去克隆一个对象,而该对象对应的类没有实现Cloneable接口,就会抛出CloneNotSupportedException异常。被检查异常通常都是可以恢复的。
如:
IOException
FileNotFoundException
SQLException
被检查的异常适用于那些不是因程序引起的错误情况,比如:读取文件时文件不存在引发的FileNotFoundException 。然而,不被检查的异常通常都是由于糟糕的编程引起的,比如:在对象引用时没有确保对象非空而引起的NullPointerException
3、错误
定义 : Error类及其子类。
特点 : 和运行时异常一样,编译器也不会对错误进行检查。
当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。出现这种错误会导致程序终止运行。
OutOfMemoryError、ThreadDeath
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等

1.21 OOM你遇到过哪些情况,SOF你遇到过哪些情况

1,OutOfMemoryError异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能。
Java Heap 溢出:
一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess
java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。
出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GCRoots的引用链。于是就能找到泄漏对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当

2,虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
这里需要注意当栈的大小越大可分配的线程数就越少

3,运行时常量池溢出
异常信息java.lang.OutOfMemoryError:PermGenspace
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。该方法的作用是:如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

4,方法区溢出
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。也有可能是方法区中保存的class对象没有被及时回收掉或者class信息占用的内存超过了我们配置。
异常信息:java.lang.OutOfMemoryError:PermGenspace
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在经常动态生成大量Class的应用中,要特别注意这点。
SOF(堆栈溢出StackOverflow
StackOverflowError 的定义:当应用程序递归太深而发生堆栈溢出时,抛出该错误。
因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m而导致溢出。
栈溢出的原因:递归调用,大量循环或死循环,全局变量是否过多,数组、List、map数据过大。

1.22 socket的过程

​ Socket是网络通信中使用的一种机制,它可以实现两个不同的进程之间的通信。下面是Socket的通信过程: 服务器端启动,绑定一个端口号,等待客户端的连接请求。 客户端启动,向服务器端发送连接请求,并指定服务器的IP地址和端口号。 服务器端接收到客户端的连接请求后,为该连接创建一个新的Socket对象,同时服务器端继续等待其他客户端的连接请求。 客户端接收到服务器端的响应后,为该连接创建一个新的Socket对象,然后通过该Socket对象与服务器端进行通信。 服务器端和客户端通过各自的Socket对象进行通信,直到通信结束。
通信结束后,客户端和服务器端分别关闭自己的Socket对象,释放网络资源。 在通信过程中,Socket对象扮演着重要的角色,它通过网络传输数据并完成通信。在Java中,可以使用Java Socket API实现Socket通信。

1.23 HTTP VS HTTPS

HTTP(Hypertext Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)是用于在互联网上传输数据的两种不同协议。它们之间的主要区别在于安全性:

  1. 安全性:
    • HTTP:HTTP是一种不安全的协议,数据在传输过程中以明文形式传输。这意味着如果有人截获了传输的数据,他们可以轻松地读取其中的内容,这对于敏感信息(如登录凭据、信用卡信息等)来说是不安全的。
    • HTTPS:HTTPS通过使用SSL/TLS加密协议来加密传输的数据,因此数据在传输过程中是加密的。这提供了更高的安全性,使得数据更难以被第三方截获或窃取。HTTPS通常用于安全性要求较高的网站,如银行、电子商务和社交媒体网站。
  2. 端口:
    • HTTP默认端口为80,因此HTTP请求的URL通常不包含端口号。
    • HTTPS默认端口为443,因此HTTPS请求的URL通常会包含端口号443。
  3. 证书:
    • HTTPS需要网站拥有有效的SSL/TLS证书,证明该网站的身份。浏览器会验证证书的有效性,以确保用户正在访问的网站是合法的。如果证书无效或已过期,浏览器会发出警告。
    • HTTP不需要证书,因此无法提供对网站身份的验证,容易受到中间人攻击。
  4. SEO和信任:
    • 搜索引擎通常更喜欢排名HTTPS网站,因为它们提供更高的安全性。因此,采用HTTPS可能有助于提高网站的搜索引擎排名。
    • HTTPS传输的数据更可信,因为用户可以通过浏览器的地址栏看到安全锁图标,表示连接是安全的。

总之,HTTPS是一种更安全的协议,适用于需要保护数据隐私和网站完整性的情况,而HTTP通常用于不涉及敏感信息的一般数据传输。在今天的互联网中,推荐在网站上启用HTTPS,以提供更好的用户安全性和隐私保护。

1.24 HTTPS的工作流程

HTTPS 默认工作在 TCP 协议443端口,它的工作流程一般如以下方式:

  • 1、TCP 三次同步握手

  • 2、客户端验证服务器数字证书

  • 3、DH 算法协商对称加密算法的密钥、hash 算法的密钥

  • 4、SSL 安全加密隧道协商完成

  • 5、网页以加密的方式传输,用协商的对称加密算法和密钥加密,保证数据机密性;用协商的hash算法进行数据完整性保护,保证数据不被篡改。

    TCP 三次握手/

    在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接

    img

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 第一次握手:客户端尝试连接服务器,向服务器发送 syn 包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入 SYN_SEND 状态等待服务器确认
  • 第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个 SYN包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态
  • 第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

HTTPS 的工作原理

我们都知道 HTTPS 能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用 HTTPS 协议。

img

1.25 HTTP状态码

HTTP状态码是HTTP协议中用于表示请求状态的三位数字代码,以下是一些常见的HTTP状态码及其含义:

  1. 1xx(信息性状态码):表示请求已被接收,继续处理。
    • 100 Continue:服务器已接收到请求的初始部分,客户端可以继续发送请求的其余部分。
  2. 2xx(成功状态码):表示请求已成功被服务器接收、理解、并接受。
    • 200 OK:请求成功,服务器已返回请求的内容。
    • 201 Created:请求已成功,服务器已创建新资源,并返回新资源的信息。
    • 204 No Content:请求成功,但服务器没有返回任何内容。
  3. 3xx(重定向状态码):表示客户端需要采取进一步的操作才能完成请求。
    • 301 Moved Permanently:请求的资源已永久移动到新的URL。
    • 302 Found:请求的资源暂时移动到新的URL。
    • 303 See Other:请求的资源可以在另一个URL下找到。
    • 304 Not Modified:资源未修改,客户端可以使用缓存的内容。
  4. 4xx(客户端错误状态码):表示客户端发送的请求有误或无法完成。
    • 400 Bad Request:请求无效,服务器无法理解。
    • 401 Unauthorized:未授权,需要提供身份验证信息。
    • 403 Forbidden:禁止访问,服务器拒绝请求。
    • 404 Not Found:未找到请求的资源。
  5. 5xx(服务器错误状态码):表示服务器在处理请求时发生错误。
    • 500 Internal Server Error:服务器内部错误。
    • 501 Not Implemented:服务器不支持请求的功能。
    • 502 Bad Gateway:服务器作为网关或代理时从上游服务器接收到无效的响应。
    • 503 Service Unavailable:服务器当前无法处理请求,通常是因为过载或维护。

这些是一些常见的HTTP状态码,还有其他更多的状态码用于表示不同的情况。客户端通过解析HTTP响应中的状态码来了解请求的结果,并根据不同的状态码采取相应的操作。这有助于确保在Web应用程序中有效地处理不同的HTTP请求和错误情况。

UDP和TCP的区别

UDP(User Datagram Protocol)和TCP(Transmission Control Protocol)是两种用于在计算机网络中传输数据的不同协议,它们有一些重要的区别:

  1. 连接性:
    • TCP是面向连接的协议,它要求在数据传输之前建立一个连接。这种连接是可靠的,确保数据按顺序传输并且没有丢失。
    • UDP是无连接的协议,它不需要在数据传输之前建立连接。UDP发送数据包而不进行握手,因此没有可靠性保证,可能会导致数据包的丢失或乱序。
  2. 可靠性:
    • TCP提供可靠的数据传输。如果数据包丢失或损坏,TCP会尝试重新传输它,直到接收端确认收到正确的数据。
    • UDP不提供可靠性,数据包可能会丢失或按不同的顺序到达接收端,因此需要在应用层处理任何必要的纠正或丢失数据包的逻辑。
  3. 速度和效率:
    • UDP通常比TCP更快,因为它没有连接建立和维护的开销,并且没有数据重传机制。这使得UDP适用于一些实时应用,如音频和视频流。
    • TCP的可靠性和数据完整性会导致额外的开销和延迟,适用于需要确保数据完整性的应用,如文件传输和网页浏览。
  4. 应用领域:
    • TCP常用于需要可靠数据传输的应用,如电子邮件、文件传输、Web浏览等。
    • UDP常用于实时应用,如在线游戏、视频流、语音通信等,其中速度和低延迟更重要。
  5. 头部开销:
    • UDP的头部开销比TCP小,因此在传输小量数据时,UDP可能更高效。
    • TCP的头部包含更多的控制信息,确保了数据的可靠传输,但也增加了数据包的大小。

总的来说,选择使用TCP还是UDP取决于应用的需求。如果数据的可靠性和顺序非常重要,那么TCP是更好的选择。如果速度和低延迟更重要,并且可以容忍一些数据丢失,那么UDP可能更适合。有些应用甚至会同时使用两种协议,根据具体情况选择合适的传输方式。

JVM基础

TCP会尝试重新传输它,直到接收端确认收到正确的数据。

  • UDP不提供可靠性,数据包可能会丢失或按不同的顺序到达接收端,因此需要在应用层处理任何必要的纠正或丢失数据包的逻辑。
  1. 速度和效率:
    • UDP通常比TCP更快,因为它没有连接建立和维护的开销,并且没有数据重传机制。这使得UDP适用于一些实时应用,如音频和视频流。
    • TCP的可靠性和数据完整性会导致额外的开销和延迟,适用于需要确保数据完整性的应用,如文件传输和网页浏览。
  2. 应用领域:
    • TCP常用于需要可靠数据传输的应用,如电子邮件、文件传输、Web浏览等。
    • UDP常用于实时应用,如在线游戏、视频流、语音通信等,其中速度和低延迟更重要。
  3. 头部开销:
    • UDP的头部开销比TCP小,因此在传输小量数据时,UDP可能更高效。
    • TCP的头部包含更多的控制信息,确保了数据的可靠传输,但也增加了数据包的大小。

总的来说,选择使用TCP还是UDP取决于应用的需求。如果数据的可靠性和顺序非常重要,那么TCP是更好的选择。如果速度和低延迟更重要,并且可以容忍一些数据丢失,那么UDP可能更适合。有些应用甚至会同时使用两种协议,根据具体情况选择合适的传输方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值