真题链接:用友校招笔试真题_Java工程师_牛客网
git branch -a 查看所有分支信息
git branch -r 查看所有远程分支
git branch 只显示本地分支
git branch -d 用于删除分支
信号量 = 可用资源数 - 等待资源进程数
正确答案为B;
装饰模式是一种结构型设计模式,它的主要特点是:
- 可以动态地给对象增加功能
- 比继承更灵活
- 遵循开闭原则
- 不改变原有对象的基础上进行功能扩展
脏读:在同步启动的A、B两个事务中,A事务执行读操作,读到原始数据,B事务执行写操作修改原始数据,A事务再次执行读操作,能够读到B事务未提交的事务修改,所以为脏读。【读未提交,最低级别,任何情况都无法保证,所以会出现脏读、不可重复读、幻读】
不可重复读:在同步启动的A、B两个事务中,A事务执行读操作,读到原始数据,B事务执行写操作修改原始数据,B事务提交事务。A事务再次执行读操作,读取到B提交事务修改后的数据,导致在A事务中两次读取数据不一致,则为不可重复读。【读已提交,能够解决脏读,但是像这样无法解决不可重复读】
幻读:在同步启动的A、B两个事务中,A事务执行读操作,读到原始数据,B事务执行写操作修改原始数据并且新增一条数据,B事务提交事务。A事务再次执行读操作,读到没有经过B事务执行写操作修改的原始数据,且没有读到最新插入的数据,导致A事务两次读取的数据除了修改外不是最新的数据,则为幻读。【可重复读,能够解决脏读、不可重复读,但是无法解决幻读】
串行化,可以解决脏读、不可重复读、幻读等所有问题,因为在这个机制下只有一个事务能够执行并锁定。
JDK1.8中ConcurrentHashMap采用了数组+链表+红黑树的复合数据结构,这是其内部存储的基本架构。 当哈希冲突时,会首先使用链表来存储,当链表长度超过阈值(默认为8)时,链表会转换为红黑树,以提高检索效率。【初始状态是数组,当发生哈希冲突时使用链表,当链表长度超过阈值后转换为红黑树,这三种结构共同组成了完整的存储体系。】
protected 访问修饰符在Java中具有特定的访问权限范围:
-
- 类内可访问 - 在定义protected成员的类内部可以直接访问
- 包内可访问 - 同一个包中的其他类可以访问protected成员
- 子类可访问 - 不同包中的子类可以访问父类的protected成员
- 任意类不可访问 - 非以上三种情况的其他类无法访问protected成员
在Java线程编程中,setDaemon()和join()方法的使用时机非常重要,setDaemon(true)必须在线程启动前调用,这是由Java线程的生命周期决定的。一旦线程启动后,就不能再修改它的守护线程状态。
- 守护线程是为其他线程服务的线程,当所有非守护线程结束时,守护线程会自动终止
- join()方法常用于线程同步,可以让一个线程等待另一个线程完成后再继续执行
join()方法是用来等待线程结束的,它的调用位置应该在需要等待的线程代码中,而不是必须在start()之前。实际上,join()通常在start()之后调用,因为它的作用是等待目标线程执行完成之后在在执行。
什么是泛化?
在面向对象设计中,泛化关系表示一般与特殊、一般与具体之间的关系,也就是继承关系。泛化关系要求父类是子类的一般化概念,子类是父类的特殊化实例。
A错误:人员类型是用来对公司人员进行分类的属性,技术人员与人员类型之间不是继承关系,而是一种从属关系。
B错误:虽然公司人员与技术人员、管理人员、行政人员之间确实构成泛化关系,但选项中提到"相互转换"的说法不准确。在泛化关系中,子类之间通常不能相互转换。
D错误:部门是公司人员的一个属性,各部门人员与公司人员之间是一种组织结构关系,而不是泛化关系。一个人员可以调换部门,说明这是一种动态的归属关系,而不是继承关系。
根据上图可知
shutdown()方法会让线程池进入"关闭"状态,此时不再接受新的任务提交,但会继续执行队列中的任务直到完成。这是一种平缓的关闭方式。
shutdown()方法不是阻塞方法,它仅仅是发出关闭信号后就立即返回。
shutdownNow()方法会尝试终止所有正在执行的任务,并返回等待执行的任务列表(List)。这些任务是尚未开始执行的任务。
awaitTermination(long timeout, TimeUnit unit)是阻塞方法,它会等待直到以下三种情况之一发生:
-
-
- 所有任务执行完成
- 到达指定的超时时间
- 当前线程被中断
这个方法常用于确保线程池完全关闭。
-
Vector和HashTable都是线程安全的集合类,它们的所有方法都被synchronized关键字修饰,能够保证在多线程环境下的数据一致性。
A. ArrayList是非线程安全的集合类,它的方法没有进行同步处理。在多线程环境下对ArrayList进行并发操作可能会导致数据不一致。如果需要线程安全的ArrayList,可以使用Collections.synchronizedList()方法进行包装。
C. HashMap也是非线程安全的集合类。当多个线程同时操作HashMap时,可能会导致数据丢失或不一致。如果需要在多线程环境下使用Map,应该使用ConcurrentHashMap或者HashTable。
补充说明:
- Vector是ArrayList的线程安全版本,但由于synchronized关键字导致的性能开销,现在更推荐使用ArrayList+Collections.synchronizedList()或CopyOnWriteArrayList。
- HashTable是HashMap的线程安全版本,但同样由于性能原因,现在更推荐使用ConcurrentHashMap,它提供了更好的并发性能。
- 虽然Vector和HashTable是线程安全的,但它们的迭代器不是线程安全的,在迭代过程中如果集合被修改,仍然会抛出ConcurrentModificationException。
final不能修饰接口和抽象类。final表示"最终的"含义,而接口和抽象类本身就是需要被实现或继承的,与final的语义相矛盾。final只能修饰具体的类、方法和变量。
final修饰的方法不能被重写(Override),但是可以被重载(Overload)。重写是子类对父类方法的覆盖,而重载是同一个类中方法名相同但参数不同。
final修饰的类确实不能被继承,这是Java中实现封装的重要手段之一。
final修饰的变量是常量,一旦被赋值就不能再次修改。对于基本类型,是值不能改变;对于引用类型,是引用不能改变(但对象的内容可以改变)。
总结:
final关键字的核心作用是实现"不可变性",但具体的"不可变"含义要根据修饰对象来具体分析:
- 修饰类:不能被继承
- 修饰方法:不能被重写
- 修饰变量:不能被再次赋值
B+树非常适合范围查询。因为B+树的所有数据都存储在叶子节点,且叶子节点之间通过指针连接形成有序链表,可以很方便地实现范围查询操作。
B+树的查询性能确实稳定。因为B+树是平衡树结构,无论查询任何数据,树的高度都是相同的,所以每次查询的时间复杂度都是固定的O(log n),这保证了查询性能的稳定性。B+树正是为了避免线性查找而设计的数据结构。
对于稀疏矩阵的压缩存储,三元组和十字链表都是常用且有效的压缩存储方法。
三元组法是最基本的稀疏矩阵压缩存储方法。它只存储矩阵中的非零元素,每个非零元素用(行号,列号,值)三个数据项表示,大大节省了存储空间。
十字链表是一种链式存储结构,将同一行/列的非零元素用双向链表串起来,既保持了矩阵的结构特性,又实现了压缩存储的目的。
二维数组是稀疏矩阵的顺序存储方式,需要存储所有元素(包括大量的零元素),不属于压缩存储方法。
散列(哈希)是一种查找技术,主要用于快速存取数据,不是专门用于压缩存储稀疏矩阵的方法。
编程题
24.计算兔子数量
有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,求第n个月的兔子总对数为多少?
你的答案:
提交时间:2025-03-23 语言:Java 运行时间:25ms 占用内存:9864 状态:编译正确
importjava.util.*;
publicclassSolution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算第n个月的兔子总共多少对。
* @param count int整型 第多少个月后兔子总对数
* @return long长整型
*/
publiclongcalculateTotal (intcount) {
// write code here
returnnum(count);
}
privateintnum(intcount) {
if(count == 1|| count == 2){
return1;
}else{
returnnum(count - 1) + num(count - 2);
}
}
}
官方解析:暂无官方题目解析。
编程题
25.岛屿的最大面积
给你一个大小为 m x n 的二进制矩阵 grid。
岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
岛屿的面积是岛上值为 1 的单元格的数目。
计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
上图的非蓝色块并且表示1的为岛屿
你的答案:
提交时间:2025-03-23 语言:Java 运行时间:41ms 占用内存:10928 状态:编译正确
importjava.util.*;
publicclassMain {
publicstaticvoidmain(String[] args) {
Scanner in = newScanner(System.in);
List<int[]> gridList = newArrayList<>();
while(in.hasNextLine()){
String line = in.nextLine().trim(); // 删除字符串的头尾空白符
String[] parts = line.split(",");
intlen = parts.length;
int[] rows = newint[len];
for(inti = 0; i < len; i++) {
rows[i] = Integer.valueOf(parts[i]);
}
gridList.add(rows);
}
int[][] grid = newint[gridList.size()][gridList.get(0).length];
for(inti = 0; i < gridList.size(); i++) {
grid[i] = gridList.get(i);
}
intmaxArea = 0;
for(inti = 0; i < grid.length; i++) {
for(intj = 0; j < grid[0].length; j++) {
if(grid[i][j] == 1){
intarea = getMaxArea(grid,i,j);
maxArea = Math.max(area,maxArea);
}
}
}
// 正确代码应添加
System.out.println(maxArea);
}
privatestaticintgetMaxArea(int[][] grid, inti, intj) {
if(i < 0|| i >= grid.length || j < 0|| j >= grid[0].length || grid[i][j] != 1){
return0;
}
grid[i][j] = 0;
returngetMaxArea(grid, i+1, j) + getMaxArea(grid, i-1, j) + getMaxArea(grid, i, j+1) + getMaxArea(grid, i, j-1) + 1;
}
}
官方解析:暂无官方题目解析。
编程题
26.
使用堆栈知识计算可以看见几栋楼楼顶
城管队员需要查看,楼顶是否有加盖的违规建筑,一条步行街上有很多高楼,共有n座高楼排成一行。城管队员第一次需要统计每一栋楼顶上可以看见几栋楼的楼顶(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住),便于以后只选择到几栋楼,就可以看见全部高楼的楼顶是否有违建。
如步行街上有高度依次为{50m, 30m, 80m, 30m, 27m, 57m}的六栋大楼,当站在第3栋大楼上时,可以看见5栋大楼顶部是否有违规建筑(当处于第3栋楼,可以向前看到位置2、1处的楼,向后看到位置4、6处的楼,加上第3栋楼,供可看到5栋楼)。请用代码求解,当大楼的高度依次为数组[a1,a2,...an]时,站在每一个大楼上可以看见的大楼楼顶数量的数组[b1,b2,...bn]。
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 采用单调递减栈,栈顶元素最小,因为前面楼层低,不会挡住后面;前面高,才会挡住后面,栈顶是最接近当前位置的楼,所以要最小,代表此时栈顶所在位置向某一方向能看到其它楼的楼顶。
* @param heights int整型一维数组 n座楼的楼层高度
* @return int整型一维数组
*/
public int[] findBuilding (int[] heights) {
// write code here
int n = heights.length;
int[] ans = new int[n];
Stack<Integer> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
ArrayList<Integer> count1 = new ArrayList<>();
ArrayList<Integer> count2 = new ArrayList<>();
for(int i = 0, j = n-1; i < n && j>=0; i++, j--) {
count1.add(stack1.size());
count2.add(0, stack2.size());
while(!stack1.empty() && stack1.peek() <= heights[i]) stack1.pop();
while(!stack2.empty() && stack2.peek() <= heights[j]) stack2.pop();
stack1.push(heights[i]);
stack2.push(heights[j]);
}
for(int i = 0;i<n;i++){
ans[i] = count1.get(i) + count2.get(i) + 1;
}
return ans;
}
}