JAVA基础
2023-12-13 15:43:52 0 举报
面试突击
作者其他创作
大纲/内容
谈谈你对面向对象和面向过程的理解:
面向过程:
汇编语言是对底层机器语言的轻微抽象 接着就出现了许多命令式的语言(C BASIC )等都是对汇编语言的抽象这些语言在汇编语言上有了大量的改进 但它所做的主要抽象在解决问题时仍然是要基于计算机的结构(一个一个函数顺序执行),而不是基于要解决的问题的结构来考虑使得我们必须建立起机器模型(位于“解空间内”,这是你对问题建模的地方,列如计算机)和实际待解问题的模型(位于“问题空间内”这是问题存在的地方,列如一项业务)之间的关联 这种映射是非常费力的 编程语言没有提供给我们 这种程序难以编写 并且维护代价非常高 同时也产生了作为副产物的整个“编程方法”行业简单的来说 函数名只能描述它的行为 最终完成整个程序的时候 你需要自己去组件关联 代码的阅读性差 可复用性差 所以维护成本高
汇编语言是对底层机器语言的轻微抽象 接着就出现了许多命令式的语言(C BASIC )等都是对汇编语言的抽象这些语言在汇编语言上有了大量的改进 但它所做的主要抽象在解决问题时仍然是要基于计算机的结构(一个一个函数顺序执行),而不是基于要解决的问题的结构来考虑使得我们必须建立起机器模型(位于“解空间内”,这是你对问题建模的地方,列如计算机)和实际待解问题的模型(位于“问题空间内”这是问题存在的地方,列如一项业务)之间的关联 这种映射是非常费力的 编程语言没有提供给我们 这种程序难以编写 并且维护代价非常高 同时也产生了作为副产物的整个“编程方法”行业简单的来说 函数名只能描述它的行为 最终完成整个程序的时候 你需要自己去组件关联 代码的阅读性差 可复用性差 所以维护成本高
面向对象:
程序可以通过添加新的类型使自身适用于某个特定的问题,当我们在阅读描述解决方案的代码的时候 也是在阅读问题的表述是可以使用问题描述问题 我们在读代码时能够清楚的知道 我们要 解决问题 需要哪些对象 因为这些对象本身就是问题的一部分 对象即是服务提供者而类描述了具有相同特性(数据元素)的行为(功能)的对象集合 所以类实际上就是一个数据类型 用我自己的话来说类和对象的关系就好像是 印章和章印的关系 新建一个对象就是盖一个章
多态:就是后期绑定 运行时才能确定
重写: 重载继承 子类继承基类 并拥有基类的所有成员( 除了private被隐藏 )
封装:封装呢其实就是访问控制 屏蔽掉类中的不必对外暴露的成员 这样别人很容易看出哪些东西对他们来说是重要的 哪些东西可以忽略而且你可以随心修改你隐藏的部分 而不用担心会对其他人造成影响
程序可以通过添加新的类型使自身适用于某个特定的问题,当我们在阅读描述解决方案的代码的时候 也是在阅读问题的表述是可以使用问题描述问题 我们在读代码时能够清楚的知道 我们要 解决问题 需要哪些对象 因为这些对象本身就是问题的一部分 对象即是服务提供者而类描述了具有相同特性(数据元素)的行为(功能)的对象集合 所以类实际上就是一个数据类型 用我自己的话来说类和对象的关系就好像是 印章和章印的关系 新建一个对象就是盖一个章
多态:就是后期绑定 运行时才能确定
重写: 重载继承 子类继承基类 并拥有基类的所有成员( 除了private被隐藏 )
封装:封装呢其实就是访问控制 屏蔽掉类中的不必对外暴露的成员 这样别人很容易看出哪些东西对他们来说是重要的 哪些东西可以忽略而且你可以随心修改你隐藏的部分 而不用担心会对其他人造成影响
谈谈你对集合的理解:
本质上就是数据结构 基本就是聊数据结构的优差异了 适用于什么场景
谈谈你对java的理解
是一门面向对象编程语言
一次编译 到处运行
垃圾回收机制 GC 这里就会聊到JRE JDK 新特性啥的 JVM 编译器 类加载器
或者谈谈 JVM 的一些基础概念和机制,比如 Java 的类加载机制,
常用版本 JDK(如 JDK 8)内嵌的 Class-Loader,例如 Bootstrap、 Application 和 Extension Class-loader;
类加载大致过程:加载、验证、链接、初始化
自定义 Class-Loader 等。还有垃圾收集的基本原理,
最常见的垃圾收集器,如 SerialGC、Parallel GC、 CMS、 G1 等,适用于什么样的工作负载。这些都是可以扩展开的领域
一次编译 到处运行
垃圾回收机制 GC 这里就会聊到JRE JDK 新特性啥的 JVM 编译器 类加载器
或者谈谈 JVM 的一些基础概念和机制,比如 Java 的类加载机制,
常用版本 JDK(如 JDK 8)内嵌的 Class-Loader,例如 Bootstrap、 Application 和 Extension Class-loader;
类加载大致过程:加载、验证、链接、初始化
自定义 Class-Loader 等。还有垃圾收集的基本原理,
最常见的垃圾收集器,如 SerialGC、Parallel GC、 CMS、 G1 等,适用于什么样的工作负载。这些都是可以扩展开的领域
对于“Java 是解释执行”这句话,这个说法不太准确。
我们开发的 Java 的源代码,首先通过 Javac 编译成为字节码(bytecode),
然后,在运行时,通过 Java 虚拟机(JVM)内嵌的解释器将字节码转换成为最终的机器码。
但是常见的 JVM,比如我们大多数情况使用的 Oracle JDK 提供的 Hotspot JVM,
都提供了 JIT(Just-In-Time)编译器,也就是通常所说的动态编译器,
这种情况下部分热点代码就属于编译执行,而不是解释执行了。
我们开发的 Java 的源代码,首先通过 Javac 编译成为字节码(bytecode),
然后,在运行时,通过 Java 虚拟机(JVM)内嵌的解释器将字节码转换成为最终的机器码。
但是常见的 JVM,比如我们大多数情况使用的 Oracle JDK 提供的 Hotspot JVM,
都提供了 JIT(Just-In-Time)编译器,也就是通常所说的动态编译器,
这种情况下部分热点代码就属于编译执行,而不是解释执行了。
对于 Java 平台的理解 可以从很多方面简明扼要地谈一下,
例如:Java 语言特性,包括泛型、Lambda 等语言特性;
基础类库,包括集合、IO/NIO、网络、并发、安全等基础类库JIT 能够在运行时将热点代码编译成机器码,
例如:Java 语言特性,包括泛型、Lambda 等语言特性;
基础类库,包括集合、IO/NIO、网络、并发、安全等基础类库JIT 能够在运行时将热点代码编译成机器码,
异常 Exception Error
Error一般是程序处理不了的异常 会导致jvm停止
Exception 又分为可检查异常 和 非检查异常
可检查异常就是在编译期必须处理的异常 否则会编译不通过 非检查异常 比如空指针这种 运行时异常
Exception 又分为可检查异常 和 非检查异常
可检查异常就是在编译期必须处理的异常 否则会编译不通过 非检查异常 比如空指针这种 运行时异常
线程
线程的创建方式
线程的状态
new thread() 进入新建状态
thread.start() 进入ready状态 随后进入running状态 最后terminated终止状态
Object.wait() Object.join() locksupport.part() Thread.sleep(Long) 会让线程进入等待状态
Object.notify() Object.notifyAll() locksupport.unpart(Thread) 从等待进入就绪状态
等待进入Synchronized方法/块 进入阻塞状态
获取到锁 进入就绪状态
new thread() 进入新建状态
thread.start() 进入ready状态 随后进入running状态 最后terminated终止状态
Object.wait() Object.join() locksupport.part() Thread.sleep(Long) 会让线程进入等待状态
Object.notify() Object.notifyAll() locksupport.unpart(Thread) 从等待进入就绪状态
等待进入Synchronized方法/块 进入阻塞状态
获取到锁 进入就绪状态
Synchronize原理 (没吃透 学习路线中有 后面重点看)
Synchronized 是由JVM实现的一种实现与斥同步的一种方式,如果你查看被Synchronized 修饰过的程序快编译后的字节码,会发现,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter 和 monitorexit两个字节码指念
在虚拟机执行到 monitorenter 指令时,首先要尝试获取对象的锁:如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;
当执行monitorexit 指令时将锁计数器-1:当计数器为0时,锁就被释放了。
可重入锁
偏向锁
轻量级锁
重量级锁
非公平锁
每当锁被释放后 任何一个线程都有机会竞争到锁
Synchronized 是由JVM实现的一种实现与斥同步的一种方式,如果你查看被Synchronized 修饰过的程序快编译后的字节码,会发现,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter 和 monitorexit两个字节码指念
在虚拟机执行到 monitorenter 指令时,首先要尝试获取对象的锁:如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;
当执行monitorexit 指令时将锁计数器-1:当计数器为0时,锁就被释放了。
可重入锁
偏向锁
轻量级锁
重量级锁
非公平锁
每当锁被释放后 任何一个线程都有机会竞争到锁
CAS算法
乐观锁的核心真法是CAS(CompareandSwap,比较并交换) 内存值 预期值 新值
当且仅当预期值和内存值相等时才将内存值修改为新值。
这样处理的逻辑是,首先检查某块内存的值是否跟之前我读取时的一样,如不一样则表示期间此内存值已经被别的线程更改过,舍弃本次操作,否则说明期间
没有其他线程对此内存值操作,可以把新值设置给此块内存。
CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一
乐观锁的核心真法是CAS(CompareandSwap,比较并交换) 内存值 预期值 新值
当且仅当预期值和内存值相等时才将内存值修改为新值。
这样处理的逻辑是,首先检查某块内存的值是否跟之前我读取时的一样,如不一样则表示期间此内存值已经被别的线程更改过,舍弃本次操作,否则说明期间
没有其他线程对此内存值操作,可以把新值设置给此块内存。
CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一
synchronize 和ReentrantLock 异同
1.等待可中断
2.可以判断是否有线程在排队获取锁
3.可以响应中断请求
4.可以实现公平锁
5.低竞争时使用synchronize (提问:怎么判断锁处于低竞争的情况 压测?)
1.等待可中断
2.可以判断是否有线程在排队获取锁
3.可以响应中断请求
4.可以实现公平锁
5.低竞争时使用synchronize (提问:怎么判断锁处于低竞争的情况 压测?)
线程池相关:
线程池核心构造参数
corePoolSize: 核心线程数
maximumPoolSize: 线程池允许的最大线程数
workQueue: 任务执行前保存任务的队列 保存由excute方法提交的runnable任务
keepAliveTime: 当线程池的任务小于核心线程数时 其他非核心线程的存活时间
提问: 当核心线程数满了 workQueue缓冲队列也满了 此时要新建线程立刻执行任务 执行的是刚提交的任务 还是 执行workQueue队列中的任务 并把当前任务提交到workQueue中?(从线程的无序性上来说 好像没必要把当前的任务提交到workQueue中 直接新建线程执行就可以了 效率更快)
此时的新建线程执行任务是直接由当前线程执行任务 还是当前线程创建另外一个线程?
线程池核心构造参数
corePoolSize: 核心线程数
maximumPoolSize: 线程池允许的最大线程数
workQueue: 任务执行前保存任务的队列 保存由excute方法提交的runnable任务
keepAliveTime: 当线程池的任务小于核心线程数时 其他非核心线程的存活时间
提问: 当核心线程数满了 workQueue缓冲队列也满了 此时要新建线程立刻执行任务 执行的是刚提交的任务 还是 执行workQueue队列中的任务 并把当前任务提交到workQueue中?(从线程的无序性上来说 好像没必要把当前的任务提交到workQueue中 直接新建线程执行就可以了 效率更快)
此时的新建线程执行任务是直接由当前线程执行任务 还是当前线程创建另外一个线程?
JMM内存模型
每一个线程都有自己的工作内存
创建的变量都是存储在主内存中
每一个线程都有自己的工作内存
创建的变量都是存储在主内存中
0 条评论
下一页