1、JAVA
2、计算机网络
3、计算机体系结构
4、数据库
5、计算机租场原理
6、软件工程
7、大数据
8、英文 自我介绍
1. Java
1. == 和 equals的区别
比较基本数据类型是比较的值,引用数据类型是比较两个是不是同一个对象,也就是引用是否指向同
一个对象,地址是否相同,equals本质上也是,但是可以重写这个方法,比如String和Integer类。
2. 为什么重写equals要重写hashcode?
我个人的理解是,Hash比equals方法的开销要小,速度更快,所以在涉及到hashcode的容器中(比如
HashSet),判断自己是否持有该对象时,会先检查hashCode是否相等,如果hashCode不相等,就会
直接认为不相等,并存入容器中,不会再调用equals进行比较。而hashcode的默认方法是根据内存地
址获得的,这样就会导致,即使该对象已经存在HashSet中,但是因为hashCode不同,还会再次被存
入。
因此要重写hashCode保证:如果equals判断是相等的,那hashCode值也要相等。
3. 什么是拆箱和装箱?
我理解的就是基本类型和包装类型的相互转换:
装箱就是基本数据类型可以直接赋值给包装类型,也就是Object变量
拆箱就是包装类型也可以自动转化为基本数据类型进行赋值或者比较
4. final类有什么特点?
- 不可以被继承
- final修饰的方法不可被重写
- final修饰的变量:基本数据类型值不可修改,引用数据类型指向不可发生变化
5.谈谈final、finally和finalize的区别
final是修饰成员变量、类、方法,表示不可再改变
finally是处理异常的关键字,表示最后执行
finalize是Object类的一个方法,在垃圾收集器执行的时候会执行被回收对象的这个方法,可以覆
盖此方法提供垃圾收集时的其他资源回收,例如文件关闭 等等。
6.垃圾回收
- GC是什么?为什么要有GC?
GC时垃圾收集的意思,Gabage Collection。内存处理是开发人员容易出现问题的地方,忘记或者错误
地内存回收会导致程序或者系统的不稳定甚至崩溃,Java提供的垃圾回收机制可以自动检测对象是否超
过作用域从而达到自动回收的目的。 - 垃圾回收机制
Java垃圾回收机制:不需要显示的去释放一个对象的内存,而是由JVM自动机型管理。在Jvm中有一个
低优先级的垃圾回收线程。在正常情况下是不会执行的,只有在虚拟机空闲时,或者堆内存不足时会触
发执行,扫描那些没有被任何引用的对象,将它们添加到要回收的集合中,然后进行回收操作。 - 如何判断一个对象是否存活?GC对象的判断方法?
1、 引用计数法:给每一个对象都设置一个引用计数器,当有一个地方引用这个对象时,则
count+=1,引用失效时,count-=1,当count=0时,说明此对象没有被其他对象引用,也就是死
对象。将会被GC回收。
简单,但会出现循环引用,即: A引用B,B引用A。
2、可达性分析:GC_roots作为对象的起始点,从这些节点开始向下搜索,走过的路径称为引用链,
当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象不可用的
上图中,Object1~Object4都可以被GC Root访问到,而Object5~Object7都不可以被访问到,这也就是说。也就是说,Object5、6、7这三个对象就是不可达的,下次垃圾回收的时候,可能就会被回收掉。
其实并不是所有的对象都可以作为GC Roots的对象,只有下列的对象可以作为GC Roots的对象。
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
既然是引用计数法,那肯定就有各种引用,下面来说说一些引用。
- 引用:指在程序代码之中普遍存在的,类似 Object obj = new Object() 这类的引用,只要强 引用还存在,垃圾收集器永远不会回收被引用的对象
- 软引用:用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存 溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后,提供了SoftReference类来实现软引用
- 弱引用:用来描述非必需对象,但是他的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用
- 虚引用:也被称为幽灵引用或者幻影引用。它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供了PhantomReference类来实现虚引用。供对象被finalize之后,执行指定的逻辑的机制(cleaner)
7. 关键字native
一个Native Method就是一个java调用非java代码的接口。这个接口写在java中,实现方式是用其他语
言,eg: C或C++
native不可与abstract一起使用,可以重写。
可以将native方法比作Java程序同C程序的接口,其实现步骤:
- 1、在Java中声明native()方法,然后编译;
-
- 用javah产生一个.h文件;
-
- 写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK 带的jni.h文件);
-
- 将第三步的.cpp文件编译成动态链接库文件
-
- 在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以 在Java中被访问了
6、集合
- Set如何区分重复与否?
使用equals,而不是== - 当向集合Set中增加对象时,首先集合计算要增加对象的hashCode码,根据该值来得到 一个位置用来存放当前对象。 如果在该位置没有一个对象存在的话,那么集合Set认为该对象在集合中不存在,直接 增加进去。 如果在该位置有一个对象存在的话,接着将准备增加到集合中的对象与该位置上的对象 进行equals方法比较。 如果该equals方法返回false,那么集合认为集合中不存在该对象,再进行一次散列, 将该对象放到散列后计算出来的地址中。 如果equals方法返回true,那么集合认为集合中已经存在该对象了,不再将该对象增加到集合中。
- 重写equals方法的时候必须重写hashCode方法。如果一个类的两个对象,使用equals 方法比较时,结果为true,那么这两个对象具有相同的hashCode。原因是equals方法为true,表明是同一个对象,它们hashCode当然相同。(Object类的equals方法比较的是地址)
- Object类的hashCode方法返回的是Object对象的内存地址。我们可以通
Integer.toHexString(newObject().hashCode());来得到。 - Java里面的HashSet中,如何判断两个对象是否相等?
先hashcode,如果相等则用equals,因为hashcode快。 - 判断两个对象的hashCode是否相等。 如果不相等,认为两个对象不相等。完毕 如果相等,转入2
- 判断两个对象是否equals 如果不相等,认为两个对象不相等。 如果相等,认为两个对象相等。
- 数组和集合的区别?
数组存放基本数据类型和对象引用,集合类存放对象的引用。
数组长度无法动态改变,集合可以
数组无法判断实际大小,只有长度,集合有.size()方法
集合以类的方式存在,具有封装继承多态的特性。
★ 4. Java集合类框架的基本接口有哪些?
Collection和Map(两个接口),元素集合和键值对集合。
List和Set接口继承Collection接口,
HashMap和HashTable实现了Map接口,HashTable线程安全。
上述类图中,实线边框的是实现类,比如ArrayList,LinkedList,HashMap等,折线边框的是抽象
类,比如AbstractCollection,AbstractList,AbstractMap等,而点线边框的是接口,比如
Collection,Iterator,List等。 - 说说 List,Set,Map 三者的区别?
List (对付顺序的好帮⼿): 存储的元素是有序的、可重复的。
Set (注重独⼀⽆⼆的性质): 存储的元素是无序的、不可重复的。
Map (⽤ Key 来搜索的专家): 使⽤键值对(kye-value)存储,类似于数学上的函数 y=f(x),"x"代
表 key,"y"代表 value,Key 是⽆序的、不可重复的,value 是⽆序的、可重复的,每个键最多映
射到⼀个值。 - 什么是迭代器?
Iterator是一种遍历集合类元素的对象,提供了一种通用的访问集合元素的方法,无需知道集合内部的
结构和实现细节。迭代器以内部类的形式存在每个集合类中。无法逆向访问元素, 在使用迭代器遍历集
合时,不能在集合中添加或删除元素
常用方法:
hasNext() :判断是否还有下一个元素。
next() :返回下一个元素,并将迭代器的位置向后移动。
remove() :从集合中删除迭代器返回的最后一个元素。
使用迭代器访问ArrayList:
import java.util.;
public class Test{
public static void main(String[] args) {
List list=new ArrayList();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用foreach遍历List
for (String str : list) { //也可以改写for(int
i=0;i<list.size();i++)这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为 foreach(String
str:strArray)这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator ite=list.iterator();
while(ite.hasNext())//判断下一个元素之后有值
{
System.out.println(ite.next());
}
}
}
遍历Map
import java.util. ;
public class Test{
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next(); - Iterator和ListIterator的区别?
Iterator 和 ListIterator 是Java中两种不同的迭代器实现,它们都用于遍历集合元素,但在功能上
有一些区别。 - Iterator 能用于遍历List、Set、Map等集合类的元素,而 ListIterator 只能用于遍历List集合
的元素。因此,如果要遍历Set或Map等集合,只能使用 Iterator ,而不能使用
ListIterator 。 - Iterator 只支持向前遍历集合元素,而 ListIterator 支持向前和向后遍历集合元素,可以使用
previous() 方法获取前一个元素。因此,如果需要对List集合进行双向遍历,可以使用
ListIterator ,而不能使用 Iterator 。 - ListIterator 相对于 Iterator 增加了一些额外的方法,如 add() 、 set() 、
hasPrevious() 、 previousIndex() 、 nextIndex() 等,可以在遍历过程中对集合元素进行插
入、修改、删除等操作。而 Iterator 只有 remove() 方法,只能用于删除集合元素,不能进行插
入、修改等操作。
System.out.println("key= " + entry.getKey() + " and value= " +
entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " +
entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
import java.util.ArrayList;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
// 使用ListIterator遍历List集合
ListIterator iterator = list.listIterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
// 对集合元素进行修改和插入操作
if (item.equals("Java")) {
iterator.set("Java SE"); - Java中的HashMap的工作原理?
实现了Map接口,以键值对的形式存储数据。
HashMap需要哈希函数对key计算hash值,使用对象的hashcode和equals方法来进行存取。
不可有重复的键,重复的充要条件是equals返回true。
11.HashMap实现方式
数组+链表+红黑树
当链表长度大于 8 时,我们会对链表进行"树化"操作,将其转换成一颗红黑树(一种二叉树,左边节点
的值小于根节点,右边节点的值大于根节点)
HashMap:上面也说了,HashMap 的底层实现是数组+链表+红黑树的形式的,同时它的数组的默认初
始容量是16、扩容因子为0.75,每次采用2 倍的扩容。 也就是说,每当我们数组中的存储容量达到75%
的时候,就需要对数组容量进行2 倍的扩容。
iterator.add("Java ME");
}
}
// 再次使用ListIterator遍历List集合
System.out.println("修改后的List集合:");
iterator = list.listIterator();
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println(item);
}
}
}
Java
Python
C++
修改后的List集合:
Java SE
Java ME
Python
C++ - error 和 exception的区别
这两个我理解的是错误和异常,都是Throwable的子类,Error更加严重
Exception是程序正常运行中,可以预料到的意外情况,可以捕获,进行相应处理。
可检测异常:编译检查的部分,要不然编译不通过
不可检测异常:eg:
Error在正常运行中,不太可能出现的情况。绝大部分的Error 都会导致程序处于非正常的,不可恢
复的状态, 不需要捕获, 常见的OutOfMemoryError 是Error的子类. - abstract的method是否可同时是static,是否可同时
是native,否可同时是synchronized - 不可是static,之类需要被实现,static和子类没有关系
- 不可是native,native方法表示该方法需要用另一种依赖平台的编程语言实现,不存在子类被实现
问题。 - synchronized 是同步,然而同步是需要有具体操作才能同步的,如果像abstract只有方法声明,
那同步一些什么东西就会成为一个问题了,当然抽象方法在被子类继承以后,可以添加同步。 - 接口是否可继承接口?抽象类是否可实现接口?抽象类
是否可继承实现类?
我认为
接口可以继承接口
抽象类可以实现接口
抽象类可以继承实现类,但前提是实现类必须有明确的构造函数。
12.try中的return,finally会不会执行?return前还是
return后?
会执行,会在return 前执行。
The finally block always executes when the try block exits.`
当尝试块退出时,最终块总是执行。 - try中有return, 会先将值暂存,无论finally语句中对该值做什么处理,最终返回的都是try语句中的
暂存值。 - 当try与finally语句中均有return语句,会忽略try中return。
package top.yanzx;
/** - @Author: yanzx
- @Date: 2023/2/27 14:26
- @Description:
- int 和 Integer有什么区别?
int是基本数据类型,integer是引用数据类型,是int的包装类。八大基本类型都有相应的包装类。 - 描述一个JVM加载class文件的原理机制、
简答: JVM中类的加载是由classloader和它的子类来实现的, java classloader是一个重要的java运行系
统组件,它负责查找和装入类文件的类。
JVM(Java Virtual Machine)是一个运行Java程序的虚拟机,它负责将Java程序的字节码文件加载到内
存中,并执行这些字节码指令。下面是JVM加载class文件的基本原理机制: - 类加载器:JVM中的类加载器(ClassLoader)负责加载Java类文件到内存中。ClassLoader可以从
文件系统、网络或其他来源中加载class文件。 - 类加载过程:当JVM需要一个类时,它会先请求ClassLoader将类加载到内存中。ClassLoader会
根据类的全限定名(Fully Qualified Name)查找相应的class文件,如果找到则加载这个class文
件,否则抛出ClassNotFoundException异常。 - 类加载阶段:类加载器加载class文件时,会经历以下3个阶段:
(1) Loading(加载)阶段:在这个阶段,类加载器将class文件中的二进制数据读入到内存中,并
创建一个对应的Class对象。
(2) Linking(连接)阶段:在这个阶段,类加载器会执行以下操作:
*/
public class Demo04Try {
public static void main(String[] args) {
System.out.println(func1());
}
public static int func1(){
int x = 1;
try {
// 返回的暂存值,return会暂存x的值,finally也会执行,但修改的值和返回的值无
关。
return x;
}finally {
x +=1 ;
System.out.println(x);
}
}
}
a. 验证:验证class文件的正确性,包括字节码格式、符号引用等方面的验证。
b. 准备:为类的静态变量分配内存,并初始化为默认值。
c. 解析:将类中的符号引用转化为直接引用,即将类、方法、变量等符号解析为内存地址。
(3) Initialization(初始化)阶段:在这个阶段,类的静态变量和静态代码块会被执行,完成类的
初始化。 - 类加载器的层次结构:JVM中的类加载器形成了一个层次结构,ClassLoader可以有多个,每个
ClassLoader都有自己的父ClassLoader。当一个类需要被加载时,JVM会先从最上层的
ClassLoader开始查找,如果找不到则向下逐级查找,直到找到为止。 - 类的生命周期:类的生命周期包括加载、验证、准备、解析、初始化、使用和卸载等阶段。JVM中
的类加载器和类的生命周期是密切相关的,ClassLoader负责加载、验证、准备、解析和卸载
类,而类的初始化和使用则由JVM负责。 - 封装具有的特性
封装的作用是对象外部不能随意存取对象的内部数据,避免外部错误。只提供少量接口对内部成员变量
进行操作,因此大大减少了内部的修改对外部的影响。
特性:
数据吟唱
接口访问
实现隔离
可维护性
重用性
封装(Encapsulation)是面向对象编程中的一种重要概念,它指的是将数据和方法包装在一个类中,
隐藏实现细节,仅向外界提供有限的访问接口,从而实现数据的安全性、可靠性和易用性。封装具有以
下特性: - 数据隐藏:封装将类的数据和方法封装在一起,对外部世界屏蔽了内部的实现细节,只提供有限的
访问接口。这样可以保证数据的安全性,防止外部恶意修改数据,同时也保护了数据的完整性和一
致性。 - 接口访问:封装通过接口访问类的内部数据和方法,提供了一种良好的数据交互方式。外部世界只
能通过类的接口来访问内部数据和方法,这种限制了外部世界对类的干扰,使得类的实现更加稳定
和可靠。 - 实现隔离:封装使得类的实现细节被隐藏在类的内部,外部世界无法访问类的实现细节。这样可以
使得类的实现更加自由,可以在不影响外部世界的情况下自由修改和升级类的实现。 - 可维护性:封装提高了类的可维护性,由于封装使得类的实现细节被隐藏起来,类的内部实现可以
自由修改,而不会影响外部世界对类的使用。这样可以使得类的维护更加方便和安全。 - 重用性:封装提高了类的重用性,由于类的内部实现被隐藏起来,外部世界只能通过类的接口来访
问内部数据和方法。这样可以使得类的接口更加通用和稳定,从而提高了类的重用性。 - 什么时候应用带参构造函数?
- 需要对对象进行一次性初始化时
- 父类拥有带参构造函数,子类继承父类,也需要带参构造函数,并调用父类构造函数。
- 方法重载和重写的区别
重载(OverLoading)是多个重名函数,具有不同的参数数据类型或参数个数。是一个类中多态性的
一种体现。但不能以返回类型作为重载函数的区分标准。两个同名函数具有相同的参数类型,但是访问
权限不同或者返回类型不同是不可能的。
重写(Overriding)是子类继承父类,重写父类的方法,函数名参数和返回类型都要一样。子类所重
写方法的访问权限不能小于父类的。 - 接口隔离原则和单一原则如何理解?
强调了代码的模块化、灵活性和可维护性
单一是指接口要尽量的智能单一,不能为了实现一个接口而被迫实现不需要的方法情况。隔离的是实现
和调用,这样才能真正解决团队的并行开发问题。
避免降低接口的灵活性和可用性。 - 不适用异常处理程序会出现什么问题?
- 虚拟机会打印异常信息。
- 程序终止运行
要把异常尽早处理,这样产生的影响范围越小。因此,不要把自己能处理的异常抛给调用者。 - finally在什么时候使用?
finally是在任何情况下都会执行的部分。可以在: - 查询数据库发生异常时,关闭数据库连接。
- 文件读取时,关闭文件等等。
- 不要在里边放return,无论怎样都会返回这个值,没有意义。
- throw和throws的区别?
throw用来抛出异常,在方法体内。
throws用来声明方法可能会抛出什么异常,在函数声明之后。
简而言之,throw关键字用于抛出一个异常对象,throws关键字用于声明方法可能会抛出的异常类型。 - String和StringBuffer、StringBuilder的区别
- string和stringbuffer、stringbuiler都可一存储操作字符串
- String时不可以修改的,后两者可以修改
- stringbuffer线程安全,stringbuiler不安全,String则是非线程安全的
- 性能:由于String是不可变的,每次对String进行修改都会创建一个新的对象,这会产生大量的垃
圾对象,导致程序运行效率低下。而StringBuffer则是可变的,不需要每次都创建新的对象,因此
在处理大量字符串时,使用StringBuffer的效率要比String高。 - 多线程
- java中有几种方法可以实现线程
两种方法: - 继承Thread类,重写run()方法,创建Thread对象,调用start()方法启动线程。
- 实现Runnable接口:实现run()方法,创建thread对象实现了runnable接口的对象传进去,最后调
用start()方法。 - 使用线程池,避免频繁的创建和销毁线程
- 用什么关键修饰同步方法?
synchronized 关键字。 synchronized 修饰的方法称为同步方法,它保证在任意时刻只有一个线程可
以执行该方法,从而避免了多线程访问共享资源时出现的数据竞争和不一致性问题。
可以放在方法的生命出,也可以放在方法的内部块中。
在方法声明处使用 synchronized 关键字时,它会将整个方法体都变为同步代码块,即只有一个线
程可以进入方法体内执行代码。
在方法内部块上使用 synchronized 关键字时,它会将该代码块变为同步代码块,即只有一个线程
可以执行该代码块内的代码。
// 继承Thread类创建线程
public class MyThread extends Thread {
public void run() {
System.out.println("线程启动了");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
// 实现Runnable接口创建线程
public class MyRunnable implements Runnable {
public void run() {
System.out.println("线程启动了");
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
public class MyCounter { - 为什么不推荐使用stop()和suspend()方法
stop() 方法会强制终止一个线程,可能导致一些数据不一致或资源未释放的问题,可能会破坏程
序的稳定性和完整性。因此,推荐使用 interrupt() 方法来终止线程的执行,因为 interrupt()
方法会给线程一个中断信号,让线程自行判断是否终止执行,并且可以在执行过程中清理资源和状
态。
suspend() 方法会暂停线程的执行,但不会释放线程占用的锁,这可能会导致死锁的发生。因
此,推荐使用 wait() 和 notify() 方法来实现线程之间的协作和通信。 - sleep()和wait()的区别
sleep()是线程类Thread的static方法,不释放锁。但会让出CPU执行时间分片。锁不释放不影响这
个过程,如果其他线程都需要这个没有释放的锁,那最终结果就是让出执行权,大家都不执行。
wait()是Object类的方法,释放锁,暂停线程。直到其他线程调用了该对象的 notify() 或
notifyAll() 方法,或者等待的时间到了。 wait() 方法主要用于多线程之间的协作和通信,如
线程间的同步、通知和等待等。
sleep() 方法可以在任意时刻使用,而 wait() 方法必须在 synchronized 块或方法中使用,否则
会抛出 IllegalMonitorStateException 异常。
如果是控制线程的执行速度和时序,可以使用 sleep() 方法;如果是实现线程间的协作和通信,
可以使用 wait() 方法。同时,使用 wait() 方法时需要注意线程安全和死锁等问题。 - 线程同步概念
private int count;
// 同步方法
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
public static void main(String[] args) {
MyCounter counter = new MyCounter();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
counter.increment();
}).start();
}
System.out.println(counter.getCount()); // 1000
}
}
package top.yanzx;
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.lang.management.ThreadInfo;
/** - @Author: yanzx
- @Date: 2023/2/27 17:12
- @Description:
-
- // Java 8之前:
- new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Before Java8");
- }
- }).start();
-
-
- new Thread( () -> System.out.println("In Java8, Lambda expression")
).start();
- new Thread( () -> System.out.println("In Java8, Lambda expression")
-
-
- 创建任何类型的对象,作为监视器
-
- 使用 synchronized()代码块或者方法
-
- done
-
-
- */
public class Demo06ThreadSeller {
public static int tickets = 100;
public static void main(String[] args) {
// lambda相当于实现了runnable的run方法
Object obj = new Object(); // 对象锁
Runnable seller = () -> {
while (true){
// 加锁
synchronized(Demo06TheadCounter.class){ // 类锁,类的字节码
// synchronized(obj){ // monitor enter 加锁
- */
-
- 同步和异步的不同,使用时间?
多线程之间、共享数据需要存取修改等操作,需要进行同步处理。
当程序执行了一个耗时较长的操作,并且需要及时返回,那么就需要使用异步,先返回结果。 - 在Monitor内部是如何做线程同步的?
监视器和锁咋Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保每一次只有一个线程执行
同步代码块。每个监视器都和一个对象引用相关联。线程在获取锁之前不能执行临界区代码。 - 锁和监视器二者区别?
监视器是一种同步模型,和java语言没有关系,其他语言也可以使用监视器来进行同步代码块的访问,
java语言是通过synchronized(应该就是你说的锁)关键字来声明同步块的,具体是怎么同步的,是使
用的监视器模型来同步的。
锁为实现监视器提供必要的支持。
Java中锁和监视器有以下区别:1.基本概念不同;2.主要作用不同;3.执行机制不同。 基本概念不同在
于,锁是对象内存堆中头部的一部分数据,任何程序都可以使用它来协调对对象的多线程访问;而监视
器是一种同步结构,它允许线程同时互斥(使用锁)和协作。 - Java中,什么是构造函数?什么是的构造函数重载?什
么是复制构造函数?
构造函数:创建对象时需要调用的函数。用于初始化对象的成员变量。默认会有无参构造函数。
构造函数重载:根据参数列表不同,具有多个构造函数。
// 临界区: 访问临界资源的那段代码 需要具有原子性
// 加锁,被锁定的区域只有一个去执行,synchronized关键字
try {
if (tickets <= 0)
break;
Thread.sleep(10); // 不会释放锁,所以会阻塞其他线程
System.out.println(Thread.currentThread().getName() +
"正在售卖第" + tickets-- + "张票");
} catch (InterruptedException e) {
e.printStackTrace();
}
}// monitor exit 释放锁
}
};
new Thread(seller, "窗口1").start();
new Thread(seller, "窗口2").start();
new Thread(seller, "窗口3").start();
new Thread(seller, "窗口4").start();
}
}
复制(拷贝)构造函数:Java不支持C++那样的复制构造函数,这个不同点时因为java返回的是引
用类型。 - Java支持多继承吗?
不支持,一个类只能继承一个父类,但是可以实现多个接口。 - 谈谈你所理解的MVC
M:model,模型,通常包含业务逻辑和数据访问逻辑,负责数据的增删改查等操作
V:view,视图层,处理页面展示。
C:controller,负责处理用户的交互事件。
降低了代码的耦合度。实现了代码的分层和模块化,使得开发过程更加可控和可维护。通过MVC模式,
可以将不同的职责分配到不同的模块中,使得整个应用程序更加灵活和可扩展。例如,可以在不改变模
型和视图的前提下,只通过更改控制器来实现新的业务需求。
比如:SpringMVC、Django、gin+gorm