layout: post title: "面试总结" date: 2018-04-27 10:36 comments: true toc: true categories: 技术学习 tags: - 面试 - Java
进程,线程
进程是资源分配的基本单位,线程是调度的基本单位 一个进程多个线程,多线程共享进程的资源,一个进程至少包含一个进程 进程销毁,它的线程不会销毁
线程执行开销小,但不利于资源的管理和保护;而进程正相反。 线程执行过程中要进行同步和互斥,因为他们共享现成的资源
位运算优先级从高到低为:按位取法~、左移<<、右移>>、按位与&、按位异或 ^ 、按位或 |
B树特征: 1.除根节点和叶子节点,每个节点子节点范围([m/2],m) 2.根节点至少两个节点 3.所有叶节点在同一层 4.有k个子节点的非根节点恰好包含k-1个关键码
操作系统命令
DIR 显示当前目录下文件 VOL 显示磁盘卷标 VER 显示Windows版本号
文件权限只有9位,文件类型不是文件权限 按出题人的意思: 文件类型(1位)+属主权限(3位)+属组权限(3位)+其他用户权限(3位) 文件类型:d(directory目录文件)、-(普通文件)、l(link符号链接文件)、b(block块设备文件)、c(char字符设备文件)、p(pipe管道文件)、s(socket管道文件)。 属主权限:文件所有者权限,可认为是文件创建者权限 属组权限:文件所有者所在组权限,可认为是同组用户权限 其他用户权限:非同组用户权限
程序执行时,必须将地址空间变为绝对地址才能访问系统分配的内存
地址重定位:操作系统把用户程序指令中的相对地址变换成为所在存储中的绝对地址的过程
地址重定位实现了:从逻辑地址到物理地址的转换
按照重定位时机分类:静态重定位、动态重定位
- 地址的静态重定位 定义:在程序运行之前,为用户程序实行了地址重定位工作 一般由操作系统中的重定位装入程序完成 重定位装入程序的输入:用户把自己的作业链接装配成一个相对于 0 编址的目标程序 过程: 重定位装入程序根据当前内存的分配情况,按照分配区域的起始地址逐一调整目标程序指令中的地址部分。目标程序在经过重定位装入程序加工之后,不仅进入到分配给自己的绝对地址空间中,而且程序指令中的地址部分全部进行了修正,反映出了自己正确的存储位置,保证了程序的正确运行 特点:在装入前实现调整 地址要有标识 每次装入都要进行定位 装入后地址不再改变(静态)
- 地址的动态重定位 定义:在程序执行寻址时进行重定位,访问地址时,通过地址变换机构改变为内存地址 用户程序原封不动的装入内存,运行时再完成地址的定位工作 动态重定位需要硬件的支持,要求系统中配备定位寄存器和加法器 特点: 程序可装入任意内存区域(不要求占用连续的内存区) 只装入部分程序代码即可运行 改变系统时不需要改变程序(程序占用的内存空间动态可变,只需要改变定位寄存器中的值即可)程序可方便共享
HashMap 与HashTable 都是Map接口的类,实现了key--value的映射 hashMap 无分类和排序,允许有NUll key 和多个null值,线程不安全 hashTable 慢,同步,不允许null 的key 和value
手写memcpy函数
考察难点:指针的++、内存重叠问题。
void* Memcpy(void* dst,const void* src,size_t count)
{
assert(dst != NULL && src != NULL && count > 0);
char* pDst = static_cast<char*>(dst);
const char* pSrc = static_cast<const char*>(src);
while (count--)
{
*pDst = *pSrc;
pDst++;
pSrc++;
}
return dst;
}
手写数据库建表,建索引,插入,查询语句
使用数据库要注意的问题
Linux命令,如开端口,开防火墙等
面试代码题
给一个包含重复元素的数据,让数据从多到少从大到小排列
public static void main(String[] args) {
int[] input={1,2,3,4,3,2,2,2,2,6,8,6};
Map<Integer,Integer> map=new HashMap<>();
for(int i=0;i<input.length;i++){
if(map.keySet().contains(input[i])){
map.put(input[i],map.get(input[i])+1);
}
else{
map.put(input[i],1);
}
}
List<Map.Entry<Integer, Integer>> entryList=new ArrayList<>(map.entrySet());
Collections.sort(entryList, new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
if(o1.getValue()!=o2.getValue())
{
return o2.getValue()-o1.getValue();
}
else {
return o2.getKey()-o1.getKey();
}
}
});
System.out.println(map);
System.out.println(entryList);
}
蛇形打印二叉树
//103 蛇形打印二叉树
/*
* 使用两个栈。一个栈存储奇数层节点,另一个栈存储偶数层节点。
* 当该层为奇数层时,叶子节点从右向左入栈。
* 当该层为偶数层,叶子节点从左向右入栈。
*/
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ans = new LinkedList<>();
if (root == null) {
return ans;
}
//奇数栈
LinkedList<TreeNode> s1 = new LinkedList<TreeNode>();
s1.push(root);
LinkedList<TreeNode> s2 = new LinkedList<>();
//当前层数
int level = 1;
while (!s1.isEmpty() || !s2.isEmpty()) {
if (level % 2 == 1) {
ArrayList<Integer> l1 = new ArrayList<>();
while (!s1.isEmpty()) {
TreeNode node = s1.pop();
if (node != null) {
l1.add(node.val);
if (node.left != null || node.right != null) {
s2.push(node.left);
s2.push(node.right);
}
}
}
if (s1.isEmpty()) {
level++;
ans.add(l1);
}
} else {
ArrayList<Integer> l1 = new ArrayList<>();
while (!s2.isEmpty()) {
TreeNode node = s2.pop();
if (node != null) {
l1.add(node.val);
if (node.left != null || node.right != null) {
s1.push(node.right);
s1.push(node.left);
}
}
}
if (s2.isEmpty()) {
level++;
ans.add(l1);
}
}
}
return ans;
}
错题集
Linux
目录/文件的满权限的形式:
drwxrwxrwx
-rwxrwxrwx
其中:(r:读取,w:写,x:执行)
数字对应:(r:4,w:2,x:1), 命令行中的三个数字对应的授权角色为owner, group, others
1. 通过数字修改权限
chmod 777 [-R]
2.符号类型修改
u: owner
g: group
o: others
a : all
chmod a+x [-R] 所有人都拥有执行权限
java基础
integer 是引用传递,但是因为integer是final的,每次对Integer的赋值操作,都是创建了一个新的对象,并且给变量赋上新的地址值
public class Main {
private static int x = 10;
private static Integer y = 10;
public static void updateX(int value) {
value = 3 * value;
}
public static void updateY(Integer value) {
value = 3 * value;
}
public static void main(String[] args) {
updateX(x);
updateY(y);
}
}
执行以上程序后,x和y的值分别是多少?10 10
类加载器初始化
类加载(先父后子,静态代码块(执行一次)---->非静态代码块(执行多次)---->成员变量---->构造函数)
static块可以出现类中的任何地方(只要不是方法内部,记住,任何方法内部都不行),并且执行是按照static块的顺序执行的。
静态代码块,当第一次用到本类时,代码块执行唯一的一次,第二次使用的时候就不在执行了
非静态代码块,每次调用类都执行
静态内容总是优先于非静态,所以静态代码块比构造方法优先执行
public class Test {
Person person = new Person("Test");
static{
System.out.println("test static");
}
public Test() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
class Person{
//当第一次用到本类时,代码块执行唯一的一次,第二次使用的时候就不在执行了
//静态内容总是优先于非静态,所以静态代码块比构造方法优先执行
static{
System.out.println("person static");
}
public Person(String str) {
System.out.println("person "+str);
}
}
class MyClass extends Test {
Person person = new Person("MyClass");
static{
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
输出结果为:
test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor
为什么输出结果是这样的?我们来分析下这段代码的执行过程:
找到main方法入口,main方法是程序入口,但在执行main方法之前,要先加载Test类
加载Test类的时候,发现Test类有static块,而是先执行static块,输出test static结果
然后执行new MyClass(),执行此代码之前,先加载MyClass类,发现MyClass类继承Test类,而是要先加载Test类,Test类之前已加载
加载MyClass类,发现MyClass类有static块,而是先执行static块,输出myclass static结果
然后调用MyClass类的构造器生成对象,在生成对象前,需要先初始化父类Test的成员变量,而是执行Person person = new Person("Test")代码,发现Person类没有加载
加载Person类,发现Person类有static块,而是先执行static块,输出person static结果
接着执行Person构造器,输出person Test结果
然后调用父类Test构造器,输出test constructor结果,这样就完成了父类Test的初始化了
再初始化MyClass类成员变量,执行Person构造器,输出person MyClass结果
最后调用MyClass类构造器,输出myclass constructor结果,这样就完成了MyClass类的初始化了
执行以上程序后,输出结果正确的是?ACBD
如果有finally和return,那就在return前进行finally.无论是否异常,都会执行finally
public class Main {
public static void main(String[] args) {
System.out.print(fun1());
}
public static String fun1() {
try {
System.out.print("A");
return fun2();
} finally {
System.out.print("B");
}
}
public static String fun2() {
System.out.print("C");
return "D";
}
}
static
遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。
最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。
静态代码块的执行顺序:静态代码块----->非静态代码块-------->构造函数
常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
public class Main {
public static void main(String[] args) {
System.out.println("A");
new Main();
new Main();
}
public Main() {
System.out.println("B");
}
{
System.out.println("C");
}
static {
System.out.println("D");
}
}
以上程序输出的结果,正确的是?DACBCB
根据类加载器加载类的初始化原理,推断以下代码的输入结果为:TestA
public class Test {
public static void main(String[] args) throws Exception{
ClassLoader classLoader=ClassLoader.getSystemClassLoader();
Class clazz=classLoader.loadClass("A");
System.out.print("Test");
clazz.forName("A");
}
}
class A{
static {
System.out.print("A");
}
}
Class clazz=classLoader.loadClass("A");
是加载class文件到内存中,并没有对类进行首次主动使用,所以没有初始化。
clazz.forName("A");
这个通过反射获取到A的内存中的数据结构对象,对类进行了首次使用,才会触发初始化;
总结:
对类的主动使用:
1. 创建类的实例
2. 访问某个类或者接口的静态变量,或者对该静态变量赋值。
3. 调用类的静态方法。
4. 反射(Class.forName)
5. 初始化类的子类
6. java虚拟机启动时被标明为启动类的类
通过子类引用父类的静态字段,不会导致子类初始化
该段代码的输入结果?AC
public class Test {
public static void main(String[] args) {
System.out.print(B.c);
}
}
class A {
public static String c = "C";
static {
System.out.print("A");
}
}
class B extends A{
static {
System.out.print("B");
}
}
常量在编译阶段会存入调用类常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量类的初始化。
输出C
public class Test {
public static void main(String[] args) {
System.out.print(B.c);
}
}
class A {
static {
System.out.print("A");
}
}
class B extends A{
static {
System.out.print("B");
}
public final static String c = "C";
}
读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池静态字段除外)时才初始化
输出 OKJD
public class Test {
public static void main(String[] args) {
System.out.println(Test2.a);
}
}
class Test2{
static {
System.out.print("OK");
}
public static final String a=new String("JD");
}
数组
数组扩容,初始时为10:int newCapacity = oldCapacity + (oldCapacity >> 1);容量变化的规则是((旧容量 * 3) / 2) + 1
JDK1.8中,执行以上程序后,该list进行了几次扩容6
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>()
for(int i=0;i<100;i++){
list.add("a");
}
}
}