java一些基础知识点

java基础

hashmap:

1,hashmap:构成原理,扩容过程,put过程,为什么长度总是2的N次方,是否线程安全;
构成原理:数组加链表
put过程://null总是放在数组的第一个链表中
      //求取哈希值
     //如果key在链表中已存在,则替换为新value,并且返回old值
     //如果key在链表中不存在,则调用add()方法。
addEntry()方法://新建一个entry对象,并且赋值
//判断是不是超过阈值,超过了就扩容。每次变成原来的两倍
扩容过程: //如果当前的数组长度已经达到最大值,则不在进行调整
     //根据传入参数的长度定义新的数组
     //按照新的规则,将旧数组中的元素转移到新数组中
      //更新临界值
      /旧数组中元素往新数组中迁移
为什么是原来的两倍://表面原因:在扩大时候,默认的是两倍的内存空间
     //根本原因是:在确定数组位置时(indexFor(in他 h,int length))是根据值计算hash码,
     然后根据hash码找到该数所在链表数组的位置i,然后遍历找值。在确定数组位置的时
     候, static int indexFor(int h, int length) {
        // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
          return h & (length-1);
        }
     返回的hash值和table数组的长度-1相与。2的n次方时,2n-1得到的二进制数的每个位上
     的值都为1(这是一个奇妙的世界),这使得在低位上&时,得到的和原hash的低位相同,
     加之hash(int h)方法对key的hashCode的进一步优化,加入了高位计算,就使得只有相同
     hash值的两个值才会被放到数组中的同一个位置上形成链表。
hashMap是线程不安全的
在添加,删除,修改的时候需要写操作,入锅多个线程的时候就会出现线程不安全,所以引入ConcurrentHashMap,java5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMap。
ConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。
Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;
而ConcurrentHashMap中则是一次锁住一个桶。(默认的是设置16个桶)

装箱和拆箱

为每种基本数据类型提供包装器类型(int -Interger)
装箱:一般常见Interger a= new Interger(5)
自动装箱:Interger a = 5;
自动拆箱:Interger a = 5;int b=a;
装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。
当 "=="运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换

类加载机制

负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例;
类加载分为三个步骤:加载,连接,初始化
类加载器除了用于加载类之外,还可用于类在java虚拟机的唯一性,即便是同样的字节码,被不同的类加载器加载之后所得到的类,也是不同的。
类加载器种类
启动类加载器,扩展类加载器,应用程序类加载器
自定义类加载器
双亲委派模型
类加载器 Java 类如同其它的 Java 类一样,也是要由类加载器来加载的;除了启动类加载器,每个类都有其父类加载器(父子关系由组合(不是继承)来实现);

双亲委派模型

所谓双亲委派是指每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载。
双亲委派模型的好处:
避免同一个类被多次加载;
每个加载器只能加载自己范围内的类;

加载

根据一个类的全限定名(如cn.edu.hdu.test.HelloWorld.class)来读取此类的二进制字节流到JVM内部;将字节流所代表的静态存储结构转换为方法区的运行时数据结构(hotspot选择将Class对象存储在方法区中,Java虚拟机规范并没有明确要求一定要存储在方法区或堆区中),转换为一个与目标类型对应的java.lang.Class对象;

连接
验证:

文件格式验证、元数据验证、字节码验证和符号引用验证;

准备

为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量将不再此操作范围内);

解析
初始化
何时触发初始化

**为一个类型创建一个新的对象实列时(比如new ,反射,序列化)
**调用一个类型的静态方法时(即在字节码中执行invokestatic指令)
**调用一个类型或接口的静态字段,或者对这些静态字段执行赋值操作时(即在字节码中,执行getstatic或者putstatic指令),不过用final修饰的静态字段除外,它被初始化为一个编译时常量表达式
**调用JavaAPI中的反射方法时(比如调用java.lang.Class中的方法,或者java.lang.reflect包中其他类的方法)
**初始化一个类的派生类时(Java虚拟机规范明确要求初始化一个类时,它的超类必须提前完成初始化操作,接口例外)
**JVM启动包含main方法的启动类时。

自定义类加载器

要创建用户自己的类加载器,只需要继承java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,即指明如何获取类的字节码流。
如果要符合双亲委派规范,则重写findClass方法(用户自定义类加载逻辑);要破坏的话,重写loadClass方法(双亲委派的具体逻辑实现)。

java热部署实现

首先谈一下何为热部署(hotswap),热部署是在不重启 Java 虚拟机的前提下,能自动侦测到 class 文件的变化,更新运行时 class 的行为。Java 类是通过 Java 虚拟机加载的,某个类的 class 文件在被 classloader 加载后,会生成对应的 Class 对象,之后就可以创建该类的实例。默认的虚拟机行为只会在启动时加载类,如果后期有一个类需要更新的话,单纯替换编译的 class 文件,Java 虚拟机是不会更新正在运行的 class。如果要实现热部署,最根本的方式是修改虚拟机的源代码,改变 classloader 的加载行为,使虚拟机能监听 class 文件的更新,重新加载 class 文件,这样的行为破坏性很大,为后续的 JVM 升级埋下了一个大坑。
另一种友好的方法是创建自己的 classloader 来加载需要监听的 class,这样就能控制类加载的时机,从而实现热部署。

热部署步骤:

1、销毁自定义classloader(被该加载器加载的class也会自动卸载);

2、更新class

3、使用新的ClassLoader去加载class

JVM中的Class只有满足以下三个条件,才能被GC回收,也就是该Class被卸载(unload):

  • 该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例。
  • 加载该类的ClassLoader已经被GC。
  • 该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法
String,StringBuffer与StringBuilder的区别

String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)

String:

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。

StringBuffer(线程安全,也可以改变)
java.lang.StringBuilde

java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。

Java容器里面的同步容器类

1)Vector、Stack、HashTable
2)Collections类中提供的静态工厂方法创建的类
注:实际上Stack是继承与Vector,HashTable是需要特别关心的容器

java内存空间是怎么分配的?

一, 对象优先在新生代Eden区分配
二, 大对象直接进入老年代
三, 长期存活对象将进入老年代(虚拟机设计了一个对象年龄计数器,该阀值默认为15)
四, 动态对象年龄判定 如果Survivor区中相同年龄所有对象大小的总和大于Survivor区空间的一半,年龄大于或等于该年龄的对象在Minor GC时将复制至老年代
五, 空间分配担保 当Minor GC时如果存活对象过多,无法完全放入Survivor区,就会向老年代借用内存存放对象,以完成Minor GC

java继承

在Java当中类不存在多继承,一个类只能继承一个类。要实现多继承,可以用内部类来间接实现多继承。但Java中的接口支持多继承(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)

接口和抽象类

接口中所有的方法都是抽象的,而抽象类中的方法包含抽象的和非抽象的
Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
接口是绝对抽象的,不可以被实例化,抽象类也不可以被实例化。
一个类可以继承多个接口,但是只能实现一个接口。

java当中的值传递和引用传递

数据类型分为两种:基本类型和对象类型
而变量类型也分为两种:基本类型和引用类型==============================================================
而引用类型的变量保存引用值,"引用值"指向内存空间的地址,代表了某个对象的引用,而不是对象本身。对象本身存放在这个引用值所表示的地址的位置。
基本类型:byte,short,int,long,double,float,boolean,char,returnAdresss.
引用类型:类对象,数组,接口类型
其他:String类似基本类型,值传递,不会改变实际参数的值。而则
String, Integer,等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。
StringBuffer和StringBulider,传递专用AtomicInteger原子整型对象

进程和线程之间的区别:

进程:是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
区别
1,一个线程只属于一个进程,而一个进程可以有一个主线程和很多其他线程。
2,线程是一种轻量级的进程,与进程相比,线程给操作系统带来侧创建、维护、和管理的负担要轻,意味着线程的代价或开销比较小。
3、线程没有地址空间,线程包含在进程的地址空间中。线程上下文只包含一个堆栈、一个寄存器、一个优先权,线程文本包含在他的进程 的文本片段中,进程拥有的所有资源都属于线程。所有的线程共享进程的内存和资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段, 寄存器的内容,栈段又叫运行时段,用来存放所有局部变量和临时变量。

创建线程的方法

1,继承Thread类
2,实现Runnable接口
3,应用程序可以使用Executor框架来创建线程池
4,还有一种方式是实现Callable接口

线程之间状态及转换

线程状态及转换

死锁

死锁是多个进程因竞争资源而造成的一种僵局
死锁产生的四个必要条件:
1,资源互斥
2,不剥夺条件
3,保持请求条件
3,循环等待

hashcode和equal()方法。

通过hashCode和equals方法保证元素的唯一性,当重写equals方法时,必须重写hashCode方法,因为如果不重写这两个方法,就会默认使用Object的方法,一般是不相同的,所以就会导致存储了重复值,与hashset、hashmap等性质冲突。
哈希表判断通过hashCode和equals方法判断元素是否相同的步骤如下所示:
先看hashCode的值是否相同,因为这个是逻辑内存地址,如果
不相同:则表示对象不相同
如果相同:继续在桶结构中执行equals方法,equals方法返回
True:表示对象相同,
Fasle:表示对象不相同
只有hashCode和equals方法都返回true,才表示对象相同

什么是Java优先级队列(Priority Queue)?

优先级队列中的元素可以按照任意的顺序插入,却总是按照排序的顺序进行检索。无论何时调用remove方法,总会获得当前优先级队列中的最小元素,但并不是对所有元素都排序。它是采用了堆(一个可以自我调整的二叉树),执行增加删除操作后,可以让最小元素移动到根。
https://www.nowcoder.com/ta/review-java/review?page=30

MyISAM和innoDB的区别:

MySQL默认的式MyISAM,三大事务存储引擎包含innoDB,innoDB支持对事务的ACID,并且实现了事务的四种隔离界别,还提供了行级锁和外键约束。MyISAM式非事务安全的,最小锁是表表锁。MyISAM支持全文类型索引,而innoDB不止全文索引。
jvm

进程和线程的区别

https://www.cnblogs.com/zhuzhu2016/p/5804875.html

需要学习:redis 消息队列,currentHashMap

java事务:

https://www.cnblogs.com/JohnABC/p/3521061.html

最小生成树

1,加边发
  1. 把图中的所有边按代价从小到大排序;
  2. 把图中的n个顶点看成独立的n棵树组成的森林;
  3. 按权值从小到大选择边,所选的边连接的两个顶点ui,viui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
  4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
2,加点法

图的所有顶点集合为VV;初始令集合u={s},v=V−uu={s},v=V−u;
在两个集合u,vu,v能够组成的边中,选择一条代价最小的边(u0,v0)(u0,v0),加入到最小生成树中,并把v0v0并入到集合u中。
重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。

红黑树和AVl树的区别:

https://blog.csdn.net/u010899985/article/details/80981053

Spring MVC基础架构

image.png

https://blog.csdn.net/qq_42780864/article/details/81461306

java当中的类加载器

https://blog.csdn.net/weixin_41891854/article/details/81583472

你可能感兴趣的:(java一些基础知识点)