测试面试总结

目录

美团

1、用户提交购买以后发生的业务

2、App首次打开和登录会发生的异常

3、关于机器学习中查准率、查全率等评价指标


字节

java基础

基础
java的公众号总结
自己的java总结

语言相关基础知识

1、标识符命名规则
字母/数字/下划线/$ 不能以数字开头
2、查看一个对象是否属于一个类或是它子类的一个对象
a instanceof b
3、strictfp:修饰一个类、接口或方法,在所声明的范围内,所有浮点数计算都是精确的,当一个类被strictfp修饰时,所有默认方法也被strictfp修饰
4、不可变类:创建类的实例后不允许修改它的值 String/包装类

String

String方法
String类中使用字符数组保存字符串
private final char value[]所以string对象是不可变的

String s="abc";
int index=s.indexOf("bc");//是否存在该字符串,存在返回其开始的index
String s1=String.valueOf(123);//int转String
char[] charS1=s1.toCharArray();//String转char
int len=s1.length();//返回长度
char char_0=s1.charAt(0);//返回index=0的位置的字符
//StringBuffer线程安全
StringBuffer sb=new StringBuffer(s);//string转为stringBuffer
sb.append("abc");
String s2=sb.reverse().toString();//stringBuffer转String
System.out.println(s2);
trim()://去除字符串两端空白。
split()://分割字符串,返回一个分割后的字符串数组。
equals()://字符串比较
Integer
Integer i1=new Integer(1);
Integer i2=Integer.valueOf("123");//字符串转数字
System.out.println(i2);
Integer i3=Integer.parseInt("234");
String i4=Integer.toString(123);//数字转字符串
Final关键字

类不可被继承、方法不可被重写、变量不可被修改(引用不可变,引用指向的内容可变)

finalize()方法

是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法
当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。


等待阻塞/同步阻塞/其他阻塞

等待阻塞wait() 对象执行wait()方法被放入等待队列
同步阻塞:线程在获取 synchronized 同步锁失败,放入锁池
其他阻塞:通过调用线程的 sleep()或 join()或发出了 I/O 请求时
锁池和等待池
锁池:存放所有等待该对象锁的线程,当持有锁的线程释放的时候,在锁池中的线程都会争夺锁,成功抢到锁的线程进入就绪状态
等待队列:当该对象执行了wait()后会被放入等待队列,当正在执行的线程执行notify()/notifyAll()方法的时候,则该队列中的线程会和锁池中的线程一起抢夺锁,抢夺成功则进入就绪状态,否则仍在等待队列中
关于锁池和等待队列


sleep和wait的区别

sleep()只是让出了cpu的时间片,但是不释放锁,当sleep的时候可以被interrupt打断,因此需要捕获interruptexception异常,同时sleep是Thread的静态方法,可以直接通过类名。方法名直接调用。时间到了会自动苏醒
wait() object方法,需要notify/notifyAll才能被唤醒。当执行wait方法的时候需要在同步代码块里面才可以。


object方法有哪些

equals(); 默认为==,String重写为对内容的比较
hashcode():把对象的内存地址通过计算得到一个int值
因为两个对象相等,则hashcode一定相等。但是如果重写了equals方法使得两个对象相等,但是计算出来的hashcode不等,违反了原则会在某种情况下导致内存泄漏

两个对象相等,hashcode一定相等
两个对象不等,hashcode不一定不等
hashcode相等,两个对象不一定相等
hashcode不等,两个对象一定不等

toString();不重写返回类名@hashcode值,String重写的话返回的是内容
clone()克隆,默认浅拷贝
wait();
notify()
notifyAll();
finalize();垃圾回收时执行

创建java对象的四种方式

1、new语句创建对象
2、调用clone()方法

了解浅拷贝和深拷贝,见下文
浅拷贝和深拷贝部分可参考博客
了解原型模式,见博客:java设计模式
部分java设计模式详解

public class Person implements Cloneable{
	private int age ;
    private String name;
    public Person(int age, String name) {//有参构造函数
        this.age = age;
        this.name = name;
    }

    public Person() {}//无参构造函数

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
    
    @Override
    protected Person clone() throws CloneNotSupportedException {
    	// TODO Auto-generated method stub
    	//此为浅拷贝,若要深拷贝,需要将该对象中的引用变量重写clone方法进行同样操作
    	return (Person)super.clone();
    	
    }
    
    
    public static void main(String[] args) throws CloneNotSupportedException {
		Person p1=new Person(12,"zhangsan");
		Person p2=p1.clone();
		
		System.out.println(p1);//Clone.Person@15db9742
		System.out.println(p2);//Clone.Person@6d06d69c
	} 
}

3、反射手段创建对象

4、反序列化手段创建对象


HashMap
HashMap<Integer,Integer> map=new HashMap<>();
map.put(1, 2);
map.put(2, 3);
map.put(3, 4);

Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();
while(it.hasNext()){
	Map.Entry<Integer, Integer>entry=it.next();
	System.out.println(entry.getKey());
	System.out.println(entry.getValue());
}

Set<Integer> keySet=map.keySet();
for(Integer i:keySet) System.out.println(i);

Collection<Integer> collectionValue=map.values();
for(Integer i:collectionValue) System.out.println(i);

1、存储键值对/可以存null/数组+链表+红黑树/线程不安全/递增2n/先检查是否扩容再插入
2、专有名词
默认初始容量:16(hashmap中桶的数量)
默认负载因子(衡量一个散列表的空间使用程度):0.75
3、HashMap的构造函数
默认初始容量和默认负载因子
指定初始容量和指定负载因子
指定初始容量和默认负载因子
4、由于规定了hashcode不同,则equal一定为false
所以在比较hashMap中的key值时首先比较的是key的hashcode值,若该桶内的元素重复才会依次使用equals比较该桶内的元素,若true则替换,若false则添加。因此equals只有在发生碰撞的时候才会使用
5、hashMap的put(key,value)方法【头插法】
key为null情况,直接保存在table的第一个位置
不为null,根据key的hashcode计算其hash值,根据该hash值判断桶位置【保证元素均匀的分布在每一个桶上】
在该桶上迭代,若存在相同key则覆盖并返回旧的value,否则头插元素
6、扩容。为了保证hashMap的效率,需要在临界点进行扩容,但是扩容实质上是重新计算元素在新table中的位置并进行复制处理
7、hashmap的底层数组长度为何为2的n次方
①计算桶时相当于直接使用hash值对长度取模,提高效率
②尽可能地减少hash冲突,尽可能地减少发生碰撞的几率

扩充:多线程扩容下的死循环问题


map是key-value形式的数据结构,谈谈这种数据结构有什么好处

1、使用散列表,不在意元素的顺序,能够快速地找到元素的数据,保证了快速存取的可能性。而数组和链表,如果不知道要查找的元素位置,则只能遍历寻找
2、 散列表工作原理:为每个对象计算出一个整数,称为散列码,根据散列码保存元素。
实现方式:数组+链表
散列冲突解决:略(开放定址法、拉链法)


LinkedHashMap

HashMap和双向链表合二为一即时LinkedHashMap
实现:在hashmap的基础上,将所有的entry节点链入一个双向链表。
很好的支持LRU算法
主要特点:有序【按照插入顺序或访问顺序有序】
标志位accessOrder值为true 访问顺序;值为false,插入顺序


ConcurrentHashMap

1.8以前
特点:线程安全,支持高并发。理想情况下支持16个线程执行并发写操作和任意数量线程的并发读操作
实现:本质上为segment数组,一个segment包含若干个桶。在并发操作时只需要锁对应的segment即可
静态内部类HashEntry的不变性、Volatile的内存可见性、加锁重读机制
HashEntry中的key/hash/next为final;value为volatile修饰
由于next为final的,所以不能在链表的中部和尾部插入,只能头插,删除时需要复制删除节点前的内容才能删除
注意:其不允许key/value为null
1.8及以后【需要补充】
去掉了segment的概念,直接对table数组元素进行加锁
将数组+单向链表结构改为数组+链表+红黑树

HashTable

特点:线程安全,效率低
数据结构:数组+链表,存储键值对,不允许键/值为null
初始11/递增2n+1/先插入再检查是否扩容
1、为何hashtable线程安全
其put操作直接使用synchronized修饰
2、与hashMap的不同
直接使用key的hashcode值取余得到桶的位置
插入前检查是否需要扩容
key/value不允许存null
扩容为2倍+1


&和&&的执行区别

java中与的逻辑运算符 &&短路与 &位运算符 当左右两边不是boolean时&表示按位操作


堆和栈的区别

:JVM垃圾回收的主要区域,新建对象的存放区域,多个线程共享
:线程独有,组成部分为虚拟机栈,本地方法栈,程序计数器。线程每执行一个方法就会在虚拟机栈中入栈一个栈帧,栈帧中包含局部变量表以及方法返回信息等。


内部类

内部类定义在一个类的内部,类似于一个类的属性定义
成员内部类、局部内部类、匿名内部类和静态内部类
静态内部类:定义在类内部的静态类
静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;

public class Outer {
    private static int radius = 1;
    static class StaticInner {
        public void visit() {
            System.out.println("visit outer static  variable:" + radius);
        }
    }
}
new Outer().StaticInner().visit();

成员内部类:定义在类内部的类(内部类没有static修饰)
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。
局部内部类:定义在方法中的内部类
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法
匿名内部类


static关键字/修饰成员变量/修饰方法/单例模式实现/代码块/内部类

方便在没有创建对象的情况下来进行调用(方法/变量),被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问 static变量也叫静态变量,在内存中被所有的对象共享,在内存中只有一个副本,当且仅当类初次加载时被初始化时,非静态变量是创建对象时被初始化。


访问修饰符

public:共有访问,对所有类可见
protected:对本包可见,对非本包的子类可见 不能修饰类
default:仅对本包可见
private:私有访问,只对同一个类可见,其余都不见 不能修饰类

transient 和 volitale

volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。volatile实现原理
序列化:java对象转换成字节序列的过程叫做对象的序列化
反序列化:字节序列恢复成java对象的过程称为对象的反序列化

transient:将不需要序列化的属性设为transient,则序列化的时候其变量值不会被保存。生命周期仅存于调用者的内存,不会序列化保存到磁盘或者网络传输(static修饰的变量同样如此)

序列化的优点:
1、数据的持久化
2、实现远程通信

数组与链表的区别

面经地址
数组:随机存取/物理地址连续/适合查找比较频繁的场景
链表:需遍历查找/物理地址不连续/适合插入删除比较多的场景

java中的集合有哪两个

注:集合只存储引用类型(Integer/String等)

CollectionMap
在这里插入图片描述

在这里插入图片描述

树的运用

红黑树
索引
堆排序

说一下JAVA的引用(强软弱虚。 我没看过JVM,直接躺平等死)

强引用 :new 对象属于强引用
软引用:内存充足时不会回收,内存不足会回收
弱引用:一旦发现即被回收
虚引用

包装类和基本类型

区别:
1、初始值不同 基本类型默认值根据变量类型的不同而不同,包装类的默认初始值为null,表明该对象目前为止没有指向任何实例
2、包装类为不可变类。
3、int i=5 //直接在栈中分配内存
Integer i=new Integer(5) //对象在堆内存中,引用变量在栈内存中
4、Integer的缓存策略
(-128–127) 直接调用相应包装类Integer.valueOf()
不在(-128–127)之内,才会new对象

Integer a=200; 
Integer b=200;
System.out.println(a==b);//false 值超过200

Integer i3=3;
Integer i4=3;
System.out.println(i3==i4);//true,值在范围内

Integer i1=new Integer(3);
Integer i2=new Integer(3);
System.out.println(i1==i2);//false,直接new出来的对象

补充
short缓存 -128-127
Long缓存 -128-127
float/double无缓存

自动装箱:JDK自动调用Integer.valueOf(100)
自动拆箱:底层调用了相应的xxxValue()

 Integer a=100 
 int b=a.intValue();

当Integer和int比较的时候,Integer会自动拆箱为int再比较,所以为true


强转规则

1、byte/char/short 运算时,首先是强转为int,对int类型进行运算,最后得到的值也是int型
short+short=int
2、boolean和基本数据类型不能强转
3、char类型转为高级类型时,会转为对应的ASCII码
8种精度不同
8 16 16 32 64 32 64
byte,short,char—> int —> long—> float —> double
boolean:8位,一个字节


浅拷贝和深拷贝

浅拷贝是指在拷贝时只拷贝对象的基本数据类型,引用数据类型仅拷贝该引用的地址到该位置
深拷贝是指在拷贝对象时,不仅拷贝基本数据类型,同时会将引用数据类型的数据在内存中重新开辟空间复制一份后,将新申请的地址赋给新的拷贝对象

怎么实现一个枚举类,泛型,Array能使用泛型吗,

泛型是一种参数化类型,它的<>里面可以放任何类型,而且不要强转,它是多态的一种体现。 泛型多用于容器中,往容器中放数据,事先约定什么类型数据,放的时候会检查,不是正确的类型放入时会报错,这样可以建立安全的数据,也避免了强制类型转换。
将类型检查从运行期提到编译器.运行时都会被擦除为 Object.,运行的时候都会在方法的入口和出口进行转换(就是发生擦除的边界位置),

接口是什么,和抽象类有啥区别,使用场景

抽象类必须有构造方法,用来被子类调用/可以有静态方法
接口一定没有构造方法/接口不能有静态方法/在接口中凡是变量必须是public static final

gbk utf-8 unicode 区别
equals和==

前者的==比较的是栈内存中存放的引用堆内存中对象的引用地址
equals比较两个值是否相等,而不是比地址
注:equals不能用于基本数据类型,只能用于类变量。对于基本数据类型要用类包装
hashcode是从object方法继承而来的,hashcode方法返回对象在内存中的地址转换成的int值

异常捕获说一下

在这里插入图片描述
java.lang.Throwable:作为所有异常的超类;

  • Exception(异常):分为 RuntimeException(运行时异常,不受检异常)和编译异常
    ①运行时异常:发生在运行阶段,编译器不会检查运行时异常,是通过修改代码来避免的,比如数组越界、空指针异常等情况NullPointerException、IndexOutOfBoundsException,RuntimeException;
    ②编译异常:如果程序出现,比如 IOException,必须对异常进行处理,否则编译不通过;
  • Error(错误):程序本身无法处理,一般都是JVM出现问题;

补充
1、try中有return时,也要执行finally中的语句,若finally中有return语句,会覆盖掉其他的return语句
2、finall块中代码不一定执行,如try块之间出现异常,程序终止;try块中执行了System.exit(0)

面向对象的三大特性是什么?并谈谈理解

封装:把描述一个对象的属性和行为的代码封装在一个“模块”中
模块化代码;隐藏实现细节,提高安全性
继承:在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并可以加入若干新的内容,或修改原来的方法使之更适合特殊的需要,这就是继承。继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系,提高了软件的可重用性和可扩展性
原则:单一继承/只能继承父类的非私有成员变量和方法
多态:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。
方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。
多态实现的三个必要条件:继承、重写、向上转型

面向对象五大基本原则

1、类功能单一
2、对于修改封闭,对于拓展开放
3、子类可以替换在父类出现的位置
4、接口分离原则

方法重写重载

重写发生在子类不想完全继承父类的方法子类有自己想要定义的行为,在参数、返回值、方法名均相同的情况下对方法进行重写(override),但是重写该方法要求满足:访问权限应大于等于父类方法的访问权限
注:override时返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类
static和final修饰的方法不能被重写
构造方法不能被重写 调用父类中被重写的方法使用super

重载发生在一个类中,表现形式为改变方法的参数顺序、参数类型、参数个数。是类编译时多态性的体现。

重载的方法不能通过访问权限、返回类型、抛出的异常作为重载条件

多态是同一个行为具有多个不同表现形式或形态的能力
实现方式:重写、继承、抽象类
必要条件:继承、重写、父类引用指向子类对象(向上转型)
重写是父类与子类多态性的表现
重载是同一个类中的多态性表现

java类执行顺序

没有父类的情况:jvm加载这个类(执行静态块)—main方法–构造块–构造方法–执行调用方法
有父类时JVM首先会检查当前类的父类是否加载,若没有则加载其父类,然后再加载自己

//父类Animal
class Animal {  
/*8、执行初始化*/  
    private int i = 9;  
    protected int j;  
 
/*7、调用构造方法,创建默认属性和方法,完成后发现自己没有父类*/  
    public Animal() {  
/*9、执行构造方法剩下的内容,结束后回到子类构造函数中*/  
        System.out.println("i = " + i + ", j = " + j);  
        j = 39;  
     }  
 
/*2、初始化根基类的静态对象和静态方法*/  
    private static int x1 = print("static Animal.x1 initialized");  
    static int print(String s) {  
        System.out.println(s);  
        return 47;  
    }  
}  
 
//子类 Dog
public class Dog extends Animal {  
/*10、初始化默认的属性和方法*/ 
    private int k = print("Dog.k initialized");  
 
/*6、开始创建对象,即分配存储空间->创建默认的属性和方法。 
     * 遇到隐式或者显式写出的super()跳转到父类Animal的构造函数。
     * super()要写在构造函数第一行 */  
    public Dog() { 
/*11、初始化结束执行剩下的语句*/
        System.out.println("k = " + k);  
        System.out.println("j = " + j);  
    }  
 
/*3、初始化子类的静态对象静态方法,当然mian函数也是静态方法*/  
    private static int x2 = print("static Dog.x2 initialized");
 
/*1、要执行静态main,首先要加载Dog.class文件,加载过程中发现有父类Animal, 
    *所以也要加载Animal.class文件,直至找到根基类,这里就是Animal*/       
    public static void main(String[] args) {  
 
/*4、前面步骤完成后执行main方法,输出语句*/ 
        System.out.println("Dog constructor"); 
/*5、遇到new Dog(),调用Dog对象的构造函数*/  
        Dog dog = new Dog();   
/*12、运行main函数余下的部分程序*/            
        System.out.println("Main Left"); 
    }  
}
java连接数据库

1、导入jar包在这里插入图片描述

2、写连接

Connection con = null;
String driver="com.mysql.cj.jdbc.Driver";
String url="jdbc:mysql://127.0.0.1:3306/abc";
Class.forName(driver);
con=DriverManager.getConnection(url,user,passwd);

3、操作

if(!con.isClosed()) {
	System.out.println("success");
	Statement statement=con.createStatement();
	String sql="select * from p";
	ResultSet rs=statement.executeQuery(sql);
	String job=null;
	String id=null;
	while(rs.next()) {
		job=rs.getString("job");
		id=rs.getString("ename");
		System.out.println(id+"\t"+job);
	}
}

JVM


类加载

类加载器
运行时加载类,主要用于将java的字节码文件动态加载进JVM中

在这里插入图片描述

双亲委派模型:保证使用不同的类加载器加载的系统类都是一致的。同时也避免了重复加载,保证系统的安全性,因为用户自定义的类加载器不可能加载本该由其父类加载的可靠类
遵守双亲委派机制的自定义类加载器方法:
继承ClassLoader类,重写findClass方法
可见性原理:子类加载器可以看到父类加载器加载的类,但是父类加载器看不到子类加载器加载的类
单一性原理:仅加载一个类一次

加载:查找并加载类的二进制数据(将类的class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区,在堆区生成一个代表这个类的java.lang.Class对象,用来封装类在方法区内的数据结构)【类加载器完成】
链接
验证 确保被加载的类的正确性
准备 为类的静态变量分配内存,并将其初始化为默认值
解析 把类中的符号引用转换为直接引用
初始化:为类的静态变量赋予正确的初始值

何时启动类加载器:预料到某个类将要被使用的时候
1、创建一个类的实例
2、访问某个类或接口的静态变量,或者对该静态变量赋值
3、调用类的静态方法
4、初始化一个类的子类
5、JVM的启动类,即文件名和类名相同的类
从哪个地方加载类
本地编译好的字节码文件
jar包中的class文件


内存泄漏和内存溢出
  • 内存溢出(out of memory): 是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给他存了long才能存下的数,就会发生内存溢出。

内存溢出场景
永久代发生内存溢出:加载了大量的class导致JVM装载的类空间不够
堆发生内存溢出:java虚拟机创建的对象太多
大量创建java线程会出现 :启动参数内存值设定过小
请求栈深度大于虚拟机允许栈深度----stackOverflowError
虚拟机栈扩展时无法申请到足够的内存空间—OutOfMemoryError
方法区溢出–PermGen space
内存中加载数据量过于庞大
代码中存在死循环或循环产生过多重复的对象实体
启动参数内存值设置过小

内存溢出解决方法:
1、修改JVM启动参数,直接增加内存
2、检查错误日志
3、对代码进行走查或分析,找出可能发生内存溢出的位置
4、使用内存查看工具动态查看内存使用情况

内存溢出分析:
内存映射分析工具确定是由于内存泄漏还是内存溢出
若为内存泄漏,则查看对象到GC Root的引用链
若无泄漏,检查虚拟机的参数是否合理。

  • 内存泄露(memory leak): 不再会被使用的对象的内存不能被回收,就是内存泄露

内存泄露原因
1、长生命周期的对象持有短生命周期的引用
2、各种提供了close()方法的对象【数据库连接/网络连接/io连接】
3、监听器
4、变量不合理的作用域

内存泄露解决方案
1、避免在循环中创建对象
2、尽早释放无用对象
3、尽量少用静态变量
4、使用字符串处理,少用string,多用StringBuffer/StringBuilder

找到内存泄露问题调查定位:jmap/jstack
查看虚拟机进程:jps
jmap/jhat/jstack/jstat/Jconsole内存监控和线程监控


java内存模型

java内存模型
Java虚拟机规范中试图定义一种Java内存模型(Java Memory Model,JMM)来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果
主内存与工作内存
可见性:是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但不具有原子性
volatile/synchronized/final实现可见性
原子性:操作的不可分割
synchronized/lock/unlock 保证原子性
有序性
volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
volatile/synchronized


java内存结构

JVM内存结构-运行时数据
在这里插入图片描述
线程私有:程序计数器/虚拟机栈(栈帧对应调用方法)/本地方法栈
虚拟机栈为虚拟机执行Java方法(也就是字节码)服务
而本地方法栈则为虚拟机使用到的Native方法服务
:JVM管理对象的核心区域,java线程共享
方法区:线程共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、及时编译器编译后的代码等数据。运行时常量池为方法区一部分


.java垃圾回收机制

java垃圾回收机制
在这里插入图片描述
判断对象是否应被回收:引用计数法/可达性分析算法(从一系列的GC Roots对象作为起始点,从这些节点开始向下搜索,若GC Roots到某个对象不可达,证明此对象不可用)
在Java语言中,可作为GC Roots的对象包括下面几种:
  1)虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2)方法区中类静态属性引用的对象。
  3)方法区中常量引用的对象。
  4)本地方法栈中JNI(即一般说的Native方法)引用的对象。
垃圾收集算法
标记清除算法:从根集合进行扫描,对存活的对象进行标记,标记完毕后再扫描整个空间未被标记的对象进行回收,无需压缩,会形成内存碎片,在存活对象比较多的情况下高效
复制算法:主要表现在survivor1和survivor2的复制回收算法
标记整理算法:在标记清除以后进行压缩处理,清除内存碎片
分代收集算法:新生代使用复制算法,老年代使用标记整理或标记清除算法
在这里插入图片描述
垃圾收集器主要就是根据四种垃圾收集算法演变出来的策略,并组合并发标记的概念,对分代垃圾回收与非分代垃圾回收做出区别。

如何减少GC出现次数

1、合理的JVM启动参数
2、对象不用时显示null
3、少用静态变量–静态变量属于全局变量,不会被GC回收
4、少显示调用System.GC()
5、累加字符串时少用String多用StringBufer和StringBuilder
使用string加字符串时会创建新的string对象,但过渡对象没有实际意义,只会增加更多垃圾。

创建string的步骤:String s=new String(“abc”);
1、检查常量池中是否存在“abc”,若不存在创建,存在进入下一步;
2、在堆内存中分配空间创建“abc”的字符串,并将该地址赋给s;

拼接字符串分两种情况:字符串拼接详细描述
1、直接拼接常量字符串,该情况会在常量池中不断的new出新的字符串,然后改变引用,会造成大量的垃圾
2、拼接字符串变量和字符串常量。该情况的底层转化为了stringBuffer,是在堆内存中重新开辟了一份空间,相当于new出来的新的对象。

6、分散对象创建或删除的空间,集中在同一时间创建大量的对象会导致内存不足情况,面对这种情况JVM会主GC。同样的在同一时间释放大量的对象也会导致空闲内存减少,JVM进行GC
7、少用finalize函数,会加大GC的工作量
8、能用基本类型就不用包装类型,如int/long/Integer/Long等

多线程

小陈笔记
多线程
面试题
面试题

进程通信方式

管道:匿名管道和命名管道
消息队列
共享内存 两个线程的虚拟内存地址指向同一块物理内存,共同操作该内存
信号量 signal=0/1
套接字


什么是死锁

死锁
是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进
死锁必要条件
互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。
预防死锁:
资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)


线程是什么,线程有哪些状态

新建状态(New):线程实例化后还从未执行 start()方法时的状态;
就绪状态(Runnable):调用线程的 start()方法启动线程,start()方法创建线程运行的系统资源,并调度线程运行 run()方法。当 start()方法返回后,线程就处于就绪状态。 处于就绪状态的线程并不一定立即运行 run()方法,线程还必须同其他线程竞争 CPU 时间,只有获得 CPU 时间才可以运行线程。因为在单 CPU 的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由 Java 运行时系统的线程调度程序(thread scheduler)来调度的。
运行状态(Running): 当线程获得 CPU 时间后,它才进入运行状态,真正开始执行 run()方法
阻塞状态(Blocked):出现在某一个线程等待锁的时候。 所谓阻塞状态是正在运行的线程没有运行结束,暂时让出 CPU,这时其他处于就绪状态的线程就可以获得 CPU 时间,进入运行状态
死亡状态(Dead): 有两个原因会导致线程死亡:run 方法正常退出而自然死亡/一个未捕获的异常终止了 run 方法而使线程猝死。
创建线程通过 start 方法进入就绪状态,获取 cpu 的执行权进入运行状态,失去 cpu 执行权会回到就绪状态,运行状态完成进入消亡状态,运行状态通过 sleep 方法和 wait 方法进入阻塞状态,休眠结束或者通过 notify 方法或者 notifyAll 方法释放锁进入就绪状态。


进程和线程区别

进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程是独立调度和分派的基本单位。线程可以当成小的线程。同时同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间等等,所以他们之间的切换开销会比较小。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)

进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。
一个进程可以有多个线程,操作系统中能同时运行多个进程。
操作系统会给进程分配内存,但是不会给线程分配,线程共享进程资源


线程同步的实现方式
  • 使用 sychronized 同步关键字修饰方法和代码块
  • 使用 volatile 修饰共享变量实现同步
  • 使用重入锁实现线程同步。
    ReentrantLock 类是可重入、互斥、实现了 Lock 接口的锁,常用方法有:ReentrantLock() : 创建一个 ReentrantLock 实例 ; lock() : 获得锁;unlock() : 释放锁
  • 使用局部变量实现线程同步。
    类 ThreadLocal 给每个线程绑定自己的值,实现每一个线程都有自己的共享变量。常用方法:
    ThreadLocal() : 创建一个线程本地变量
    get() : 返回此线程局部变量的当前线程副本中的值
    initialValue() : 返回此线程局部变量的当前线程的"初始值"
    set(T value) : 将此线程局部变量的当前线程副本中的值设置为 value
  • 使用阻塞队列实现线程同步
    当队列满时,队列会阻塞插入元素的线程,队列为空时,获取元素的线程会阻塞直到等待队列变为空。发生阻塞的插入方法为 put(),移除方法为 take()。阻塞队列类似 ArrayBlockingQueue、LinkedBlockingQueue。阻塞队列通常用来实现生产者和消费者模式。
  • 使用原子变量实现线程同步
    在 java 的 util.concurrent.atomic 包中提供了创建了原子类型变量的工具类,使用该类可以简化线程同步。 AtomicInteger 表可以用原子方式更新 int 的值, AtomicInteger 类常用方法:
    AtomicInteger(int initialValue) : 创建具有给定初始值的新的 AtomicInteger
    addAddGet(int dalta) : 以原子方式将给定值与当前值相加
    get() : 获取当前值

进程同步方式的优缺点

多线程的实现方式?有什么优缺点?

继承 Thread 类(真正意义上的线程类),是 Runnable 接口的实现。
实现 Runnable 接口,并重写里面的 run 方法。
使用 Executor 框架创建线程池。
通过实现Callable接口来创建Thread线程
一般情况下,常见的是第二种 Runnable 接口有如下好处:
避免单继承的局限,一个类实现多个接口;
适合于资源的共享

数据库

面试题
小陈数据库知识
小陈数据库语句
面试题

b树和b+树
  • b树(b-树)
    叶节点都位于同一层、根节点至少有两个子女【n个关键字,n+1个指针】
    非叶子节点存储的是数据
    b+树
    只有叶子节点存储的是数据、有一个链将叶子节点从小到大连接起来
外键

1、子表和父表使用相同的存储引擎,且禁止使用临时表
2、存储引擎只能用Innodb
参照列和外键列的数据类型相似,长度可变,但是数字长度或符号位必须相同
3、外键列和参照列必须创建索引,外键列不存在索引mysql也会自动创建

数据库攻击及应对/sql注入/Dos攻击

在输入参数时根据服务器数据库语句将所写参数与其拼接后可以绕过错误判断的情况
1、对特殊字符进行过滤转义或使用预编译的sql语句绑定变量
2、运行出错时不要把数据库返回的错误信息全部显示给用户,以防泄露服务器或数据库相关信息。

数据库中锁的类型 共享锁/排他锁
  • 基本类型锁:共享锁/排他锁
    共享锁:多个事务封锁一个共享页,任何事务都不能修改该页 select
    排他锁:仅允许一个事务封锁此页 insert/update/delete
  • 按照锁的粒度划分:行级锁/页级锁/表级锁
    表级锁(表共享读锁和表共享写锁):直接锁定整张表,锁定期间其他表无法对该表进行写操作
    特点:开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突概率高,并发度最低 MyISAM使用
    行级锁:仅对指定的记录进行加锁
    特点:开销大,加锁慢,会出现死锁,锁定力度最小,发生锁冲突概率低,并发度高
    Innodb支持行级锁(默认),表级锁
    页级锁:一次锁定相邻的一组记录。
乐观锁/悲观锁

乐观锁:修改结束要提交时才锁数据
实现方式:
1)数据版本记录机制(每一行数据多一个version)
2)使用时间戳

数据库删除语句,速度 truncate/delete/drop

在这里插入图片描述

事务的特性 ACID

原子性(A):事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚,回滚可以用回滚日志来实现
一致性©是指事务必须使数据库从一个一致性状态变成另一个一致性状态,也就是事务执行前后必须处于一致性状态。
以转账为例,假设用户 A 和 B 两者的钱加起来是 5000,那么不管 A 和 B 之间如何转账,转多少次,事务结束后两个用户的钱加起来应该还得是 5000,这就是事务的一致性。有外键约束情况时保持一致
隔离性(I):一个事务所做的修改在最终提交以前,对其它事务是不可见的。锁实现
持久性(D):一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能


mysql主从复制

主从复制:指数据从一个数据库服务器的主节点上复制到其他一个或多个从节点的数据库服务器。MySql 数据库默认采用异步复制方式。从节点数据库不用一直访问主服务器也可以实现更新数据。 MySQL 的主从复制并不是数据库磁盘上的文件直接拷贝,而是通过逻辑的 binlog 日志复制到要同步的服务器本地,然后由本地的线程读取日志里面的 SQL 语句重新应用到 MySQL 数据库中。
主要设计三个线程:
binlog 线程:负责将主服务器上的数据更改写入二进制(Binary log)中;
I/O 线程:负责从主服务器上读取二进制日志,并写入从服务器的重放(Replay log)中;
SQL 线程:负责读取重放日志并重放其中的 SQL 语句
读写分离:主服务器处理写操作以及实时性要求比较高的读操作,而从服务器负责读操作,
读写分离提高性能的原因在于:
主从服务器负责各自的读和写,极大程度缓解了锁的争用;
从服务器可以使用 MyISAM,提升查询性能以及节约系统开销;
增加冗余,提高可用性 读写分离常用代理方式来实现,代理服务器接受来自应用层传来的读写请求,然后决定转发到哪个服务器。
主从复制的几种方式:
1、同步复制:等所有的从服务器复制了再去处理其他事务
2、异步复制:无需等待从服务器
3、半同步复制:等一个从服务器复制了就去做其他的事情
疑点:如何避免脏读/幻读/不可重复读/如何分批次读取bin log的值


数据库优化方案
  • 语句优化:要提高 MySQL 的更新/插入效率,应首先考虑降低锁的竞争,减少写操作的等待时间
    在这里插入图片描述
  • 查找优化 在这里插入图片描述
  • 索引优化
    在这里插入图片描述
  • 表的优化
    在这里插入图片描述

mysql查询过程

1.客户端发送一条查询给服务器
2.服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果,否则进入下一阶段。
3.服务器进行SQL解析,预处理,再由优化器生成对应的执行计划
4.mysql根据优化器生成的执行计划,调用存储引擎的API来执行查询。
5.将结果返回给客户端。


外连接/内连接

外连接:
left join(左联接)返回包括左表中的所有记录和右表中联结字段相等的记录;

//就是只查分类表数据,但是减去跟商品详情表有联系的数据。
select * from
        commodity_classification c
        left join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
        where d.Classify_Description is null     

right join(右联接)返回包括右表中的所有记录和左表中联结字段相等的记录。

//意思:查询商品详情表的所有数据,但是要减去和商品分类表有联系的数据
select  *  from
        commodity_classification c
        right join commodity_list d
        on d.Classify_Description=c.Good_kinds_Name
        where c.Good_kinds_Name is null  

全连接:将相同的连接起来,独有部分补null
left 保留左表的值,右表无值填 null,right 相反
inner join
在这里插入图片描述

drop和delete区别;修改一个字段名
取出所有学生中80分以上的,逆序输出
按班级输出80分以上的
说说视图(数据安全、查询快、建立要消耗)
能直接修改视图吗?
两个表的内连接 左连接 右连接输出结果的区别?
把A表所有字段和内容复制到B表
insert into a select * from B
表A插入表B的语句

数据库的索引、主键设计自增、数据库建表建库, 联合索引 索引的数据结构是什么 数据库之间的连接用什么,索引优点及使用场景

数据库索引
索引原理
索引是一个特殊的文件,它们包含着对数据库里所有记录的引用指针。就是根据你指定的列,建立一个遵循一定数据结构的区域,这些区域可以快速定位到相应数据库字段所在的磁盘地址
索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快
索引优点

一】通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
二】可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
三】可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
四】在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
五】通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。

索引创建:
1)建表时创建
2)使用alter table 【主键索引/唯一索引/普通索引】

//标准语句:
ALTER TABLE table_name ADD INDEX index_name (column_list)//添加普通索引,索引值可出现多次。 
ALTER TABLE table_name ADD UNIQUE (column_list)//这条语句创建的索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。 
ALTER TABLE table_name ADD PRIMARY KEY (column_list)//该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE table_name ADD FULLTEXT index_name(olumu_name);该语句指定了索引为FULLTEXT,用于全文索引。

//针对上述数据库,增加商品分类的索引
ALTER table commodity_list ADD INDEX classify_index  (Classify_Description)

3)create index【普通索引/unique索引】

//标准语句:
CREATE INDEX index_name ON table_name (column_list)
CREATE UNIQUE INDEX index_name ON table_name (column_list)
//针对上述数据库:
CREATE INDEX classify_index  ON commodity_list (Classify_Description)

删除索引:drop index indexname on table_name
普通索引/唯一索引(列值唯一,允许为空)/主键索引(不允许为空)/全文索引(fulltext 对大数据文本进行索引)
聚集索引: 该索引中键值的逻辑顺序决定了表中相应行的物理顺序

索引使用注意点:
一般说来,索引应建立在那些将用于 JOIN,WHERE 判断和 ORDER BY 排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引
最好不要给数据库留 NULL,尽可能的使用 NOT NULL 填充数据库
应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描
in 和 not in 也要慎用,否则会导致全表扫描
like %keyword 索引失效,使用全表扫描
如果在 where 子句中使用参数,也会导致全表扫描
应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描
在使用索引字段作为条件时,如果该索引是复合索引(多列索引),那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致
建立索引的原则:
定义有主键的数据列一定要建立索引。因为主键可以加速定位到表中的某一行
定义有外键的数据列一定要建立索引。外键列通常用于表与表之间的连接,在其上创建索引可以加快表间的连接
在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的
在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间
在经常使用在 WHERE 子句中的列上面创建索引,加快条件的判断速度
在经常需要搜索的列上,可以加快搜索的速度
非空字段/字段尽量越小越好/离散值越大越好
索引缺点:删除添加更新需要维护/占物理内存
在这里插入图片描述

B+树比B树更合适实际应用的理由?
1、磁盘读写代价低
2、范围查询
索引优点:提高检索速度/唯一索引可以保证每行数据的唯一性/减少查询中分组和排序的时间
在这里插入图片描述

索引失效的几种情况:
1、组合索引不使用第一部分
2、模糊查询
3、使用or
4、mysql觉得全盘扫描比用索引快
5、在索引列做运算或使用函数


distinct使用

undo和redo

undo:事务操作前将数据先保存在undo日志中,用于回滚时使用,针对未提交的事务
redo:事务操作时将详细的操作写入在redo日志中,当出现实例故障导致数据未写到数据库中,会redo,用于恢复


索引为什么能加快查询,索引什么时候都能加快查询吗?

Innodb和MyISAM

MyISAM使用B+树作为索引结构/表级锁/不支持外键/不支持事务
Innodb 行级锁/支持外键/支持事务在这里插入图片描述


外键干啥用的?大型企业为什么不用外键?你知道大型数据库数仓怎么链接?

外键是用来建立和加强两个表数据之间的链接的一列或多列
实体完整性/参照完整性/用户自定义完整性
企业不用外键的原因


常用的增添删改 group by和order by和having

insert
update
delete
group by 按照某个字段进行分组
having 一般与group by一起使用
order by id,age DESC 降序排列,先根据id,id相同再根据age


数据库事务的隔离级别
  • 读未提交(Read Uncommitted) 事务中的修改,即使没有提交,对其它事务也是可见的。读取未提交的数据,也被称之为脏读。
    脏读:
  • 读已提交(Read Committed) 一个事务只能读取已经提交的事务所做的修改,即一个事务所做的修改在提交之前对其它事务是不可见的。已提交读隔离级别解决了脏读的问题,但是出现了不可重复读的问题。即事务 A 在两次查询的数据不一致,因为在两次查询之间事务 B 更新并提交了一条数据。
    事务前后对该数据进行读取----不可重复读
  • 可重复读(repeatable read) 保证在同一个事务中多次读取同样数据的结果是一样的。可能幻读
  • 记录数不一样–重点在删除或新增—幻读
  • 可串行化(Serializable) 强制事务串行执行。可串行化完全锁定字段,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止。是完整的隔离级别,会锁定对应的数据表格,因而会有效率的问题。

事务控制语句

begin或 start transaction:显式开启一个事务
commit:提交事务
rollback:回滚;


数据库三范式

第一范式(确保每列保持原子性)
数据库表中的所有字段都是单一属性,不可在分的,这个单一属性是由基本的数据类型所构成,如整数,浮点数,字符串等
数据库表的每一列都是不可分割的基本数据项,所有字段值都是不可分解的原子值

第二范式(确保表中的每列都和主键相关)
要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要我们设计一个主键来实现(这里的主键不包含业务逻辑)
要求实体的属性完全依赖于主关键字。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

第三范式(确保每列都和主键列直接相关,而不是间接相关,任何非主键不依赖于其他非主键,无传递依赖)
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。


数据库优化

1、使用索引 提高检索速度
2、优化sql语句 explain/避免select*/不在索引列使用函数或运算/使用limit减少返回行数
3、优化数据库对象 水平/垂直拆分表/使用中间表
4、硬件优化 cpu/内存/磁盘io/
5、mysql自身优化
6、应用优化 使用数据库连接池/查询缓存存储查询结果,若随后收到相同查询则直接返回结果
注:数据库连接池

Connection con;
 String driver = "com.mysql.jdbc.Driver";
 String url = "jdbc:mysql://localhost:3306/sqltestdb";
  String user = "root";
  String password = "123456";
  Class.forName(driver);
  con = DriverManager.getConnection(url,user,password);
  Statement statement = con.createStatement();
  String sql = "select * from emp";
  ResultSet rs = statement.executeQuery(sql);
数据库分表/分区/分库

分表:垂直/水平拆分
分区:一张表上的数据分成多个区块。数据表面还在一张表上,但是数据散列在多个位置 水平分区/垂直分区 多个硬盘处理不同的请求
分库:根据业务不同把相关表切分到不同的数据库中

sql语句
 添加一行
 insert into stu(stuName,stuAge,stuSex) values('zhao','20','man');
 增加列
 alter table tablename add columnname varchar(30)
 删除列
 alter table tablename drop columnname 
count区别

count(*) ==count(数字) 返回记录总行数,包括null
count(column) 不包括null统计

慢查询

1、开启慢查询日志
在配置文件添加慢查询的日志位置及语句最多执行多长时间会被记录到日志里
2、查看日志启动状态:show variables like “slow%”;
3、设置慢日志开启: set global slow_query_log = ON;
4、查询long_query_time 的值 :show variables like “long%”;
在这里插入图片描述

explain

explain参数解释
在这里插入图片描述

1、在执行sql语句的时候可以使用explain来看一下查询的效率
2、使用explain可以看出在执行这条语句的时候的查询类型、有没有子查询、查询相关的表、查询涉及到的字段的索引、查询中使用的索引的长度、实际使用的索引、搜索的表的行数、查询的性能[全表扫描ALL、使用索引扫描index、不用扫描表或者索引null]

linux基础知识

Linux基础知识
面试题

linux查看文件命令

cat/vi/more/less/head/tail

linux查看端口

lsof -i 端口号

netstat

linux中buffer和cache的区别
awk命令

用于对文本进行复杂格式处理
awk
awk

自己知道的Linux命令;统计文本行数命令;查找匹配文件命令

统计文本行数:wc -l
统计字数:wc -w

服务器的日志的文件,每个月生成一个日志文件
统计一下上一天的登录次数最多的用户,输出该用户当天的所有操作?
列举文件 查文件大小 查进程

列举文件 ls -al
查文件大小:wc -c
查进程:ps -ef

Linux更改环境变量有什么方法
grep用过吗

操作系统

死锁的四个必要条件

互斥/请求和保持/环路等待/不可剥夺
预防:资源共享/资源可剥夺/首先对资源进行预分配,进程在运行前一次性申请到所有资源/顺序资源分配
检测死锁:死锁定理
避免死锁:银行家算法
解除死锁:从死锁进程中剥夺资源/终止部分/全部进程

有哪些提高系统性能的方法

系统的性能是指操作系统完成任务的有效性、稳定性和响应速度
硬件:cpu(多线程)/内存/磁盘io性能/网络带宽
操作系统相关:系统安装优化/内核参数优化/文件系统优化/应用程序软件资源
数据库分表/建立索引/优化查询策略等

用户态和内核态的概念 区别 转换

用户态切换内核态:
1)系统调用
2)异常,缺页异常
3)外围设备的中断,当外围设备完成用户请求的操作后会向cpu发出相应中断信号
寻址能力不同/运算速度不同


换页方式

FIFO/LRU/OPT


哲学家进餐 无死锁算法

操作系统内存管理

分区管理/分页管理/分段管理/段页式管理

进程调度策略

先来后到/用时/优先级/响应比/
先来先服务调度算法
短作业(进程)优先调度算法
高优先权优先调度算法:非抢占式优先权算法/抢占式优先权调度算法
高响应比优先调度算法:(等待时间+要求服务时间)/要求服务时间
时间片轮转法:时间片到排队尾,保证所有用户都在一段时间内被处理
多级反馈队列调度算法:
1、应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
2、当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行
3、仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程


进程通信方式

进程间通信方式
信号量:计数器,用于网络中不同机器之间的进程间通信
共享内存:多个进程同时访问同一块内存
普通管道:半双工,数据只能单向流动,而且只能在具有父子关系的进程间使用
套接字( socket):网络中不同机器之间的进程间通信
消息队列:在消息传输过程中保存消息的容器,写权限的进程可以按照一定规则向消息队列中添加,读权限的进程可以从消息队列中读取信息

数据结构

  • 数据结构有什么常用结构,栈和队列区别,如何用两个栈实现一个队列
  • 二叉树是什么,树的遍历方式,图的遍历方式

算法

练习算法链接

PriorityQueue
M*N矩阵,从(0, 0)走到(m, n)共有多少条路径?
超过数组的一半的数
import java.util.*;
public class Solution {
    public int MoreThanHalfNum_Solution(int [] array) {
        HashMap<Integer,Integer> map=new HashMap<>();
        for(int i=0;i<array.length;i++){
            if(map.containsKey(array[i])){
                map.put(array[i],map.get(array[i])+1);
            }else{
                map.put(array[i],1);
            }
        }
        int res=0;
        Iterator<Map.Entry<Integer,Integer>> it =map.entrySet().iterator();
        
        while(it.hasNext()){
            Map.Entry<Integer,Integer> entry=it.next();
            if(entry.getValue()>array.length/2) 
            {
                res=entry.getKey();
                break;
            }
        }
        return res;
    }
}
最长连续不含重复字符子串
最大公共子串
返回一棵树的最大叶节点距离
对含有重复数字的数组去重并排序,
两个大数字符串求和输出字符串
import java.util.*;
public class Main{
    public static void main(String[] args){
        Scanner sc =new Scanner(System.in);
        while(sc.hasNextLine()){
            String s=sc.nextLine();
            String s1=s.split(",")[0];
            String s2=s.split(",")[1];
            if(s1.length()==0|s2.length()==0) System.out.println(0);else{
                 //判断其最大公共子串
            int res=0;
            for(int i=0;i<s1.length();i++){
                int max=0;
                int iIndex=i;
                for(int j=0;j<s2.length();j++){
                    if(iIndex<s1.length()){
                        if(s1.charAt(iIndex)!=s2.charAt(j)) continue;
                    else{
                        max++;
                        iIndex++;                   
                    }
                    }else{
                        break;
                    }                
                }
                if(res<max) res=max;
            }
            System.out.println(res);
            }
           
        }
    
    }
}

排序(快排/归并/冒泡/堆排序/选择排序/插入排序)

在这里插入图片描述

//插入排序 稳定 
//第一轮从第二个位置开始,若值比前面的有序数组小往前遍历
private void insertSort(int[] nums) {
		for(int i=1;i<nums.length;i++) {
			int j;
			int num=nums[i];
			for(j=i-1;j>=0;j--) {
				if(nums[j]>num) {
					nums[j+1]=nums[j];
				}else {					
					break;
				}
			}
			nums[j+1]=num;		
		}		
	}

//冒泡排序  稳定 
for(int i=nums.length-1;i>=0;i--) {
			for(int j=0;j<i;j++) {
				if(nums[j]>nums[j+1]) {
					int temp=nums[j];
					nums[j]=nums[j+1];
					nums[j+1]=temp;
				}
			}
		}

//归并排序 稳定 递归写法
	private static void mergeSort(int[] nums,int left,int right) {
		if(left<right) {
			int mid=(right+left)/2;
			mergeSort(nums,left,mid);
			mergeSort(nums,mid+1,right);
			merge(nums,left,mid,right);
		}
	}
	private static void merge(int[] nums, int left, int mid,  int right) {
		// TODO Auto-generated method stub
		int[] temp=new int[right-left+1];
		int i=left;
		int j=mid+1;
		int k=0;
		while(i<=mid && j<=right) {
			if(nums[i]<nums[j]) {
				temp[k++]=nums[i++];
			}else {
				temp[k++]=nums[j++];
			}
		}
		while(i<=mid) temp[k++]=nums[i++];
		while(j<=right) temp[k++]=nums[j++];
		k=left;
		for(int s:temp) nums[k++]=s;
	}
//快速排序 不稳定 O(nlogn)  辅助空间 O(logn)~O(n)
public class quickSort {
	public static void main(String[] args) {
		int[] nums= {2,3,1,7,4,6,3};
		quicksort(nums,0,nums.length-1);
		for(int i:nums) System.out.println(i);
	}

	private static void quicksort(int[] nums, int left, int right) {
		if(left<right) {
			int pivot=partion(nums,left,right);
			quicksort(nums,left,pivot-1);
			quicksort(nums,pivot+1,right);
		}		
	}

	private static int partion(int[] nums, int left, int right) {
		int pivot=nums[left];
		while(left<right) {
			while(left<right && nums[right]>pivot) right--;
			if(left<right) nums[left++]=nums[right];
			while(left<right && nums[left]<pivot)  left++;
			if(left<right) nums[right--]=nums[left];
		}
		nums[left]=pivot;
		return left;
	}
}

//选择排序  不稳定,从小到大排序
//第一轮将第一个位置与遍历的剩下的位置比较,如果第一个位置大了就交换
//第二轮从第二个数组位置开始
for(int i=0;i<nums.length;i++){
	for(int j=i+1;j<nums.length;j++){
		if(nums[i]>nums[j]) swap;
	}
}

假设有n个人,每次数第k个出列,出列后接着下一个人重新数,最后出列的那个人的序号
判断旋转字符串
斗地主发牌
符号配对,如{}为true,{[}为false

注:stack栈为空时stack.peek() 报错
stack常用方法:stack.push()/stack.pop()/stack.isEmpty()

import java.util.Scanner;
import java.util.Stack;

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Scanner sc=new Scanner(System.in);        
        while(sc.hasNextLine()){
            String s=sc.nextLine();
            Stack<Character> stack=new Stack<>();          
           for(int i=0;i<s.length();i++){
                if(s.charAt(i)=='('||s.charAt(i)=='['){
                    stack.push(s.charAt(i));
                }
                if(s.charAt(i)==')'){
                    //判断栈顶元素,若是则出栈,否则返回错误
                    if(!stack.isEmpty() && stack.peek()=='('){
                        stack.pop();                         
                        
                    }else{
                        System.out.println("false");
                        return ;                        
                    }                 
                }
                if(s.charAt(i)==']'){
                    if(!stack.isEmpty()&&(stack.peek()=='[')){
                        stack.pop(); 
                        
                    }else{
                        System.out.println("false");
                        return ;                     
                    }
                }                
            }            
            if(!stack.isEmpty()) System.out.println("false");
            else System.out.println("true");
        }
	}
}
找到一个数组第二大的数
死锁demo
逆波兰表达式
手撕二叉查找,写测试用例分析
找出数组中不重复的元素
打印二叉树的边界节点
输出比给定数大的下一个数字
给定一个正序的有重复的数组,给定一个数字,快速定位出该数字的位置
机器人走方格
字符串倒序
经典爬楼梯
在一个字符串中匹配一个子串,使词典中所有词都出现,用了一个set就解决了
输出一个数组的所有子集,代码做单元测试,怎么测试
无重复子串
第一千个丑数
链表交点,有环怎么办
逆序输出一个整数,不用已有函数
字符串数组的最长公共前缀leetcode14
8皇后问题
统计一个字符串的词频,并按照词频逆序打印输出
驼峰样式的字符串, 过滤掉类似驼峰样式,最终输出过滤后的字符串
区间合并
class Mycompare implements Comparator<int[]>{
	@Override
	public int compare(int[] o1, int[] o2) {
		// TODO Auto-generated method stub
		return o1[0]-o2[0];
	}	
}

		Scanner sc =new Scanner(System.in);
        String[] s=sc.nextLine().split(" ");
        int[][] nums=new int[s.length][2];
        int i=0;
        for(String ss:s){        	
            nums[i][0]=Integer.parseInt(ss.split(",")[0]);
            nums[i++][1]=Integer.parseInt(ss.split(",")[1]);
        } 
        
       //Arrays.sort(nums,(a,b)->Integer.compare(a[0],b[0]));
//        Comparator<int[]> cp = new Comparator<int[]>() {
//        	
//        	public int compare(int[] o1, int[] o2) {
//        		return o1[0]-o2[0];
//        	}
//		};
        Arrays.sort(nums,new Mycompare());
       List<String> list =new ArrayList<>();
        for(int j=0;j<nums.length;){
            int left=nums[j][0];
            int right=nums[j][1];
            while(j<nums.length-1&&nums[j+1][0]<=right){
                j++;
                right=Math.max(right,nums[j][1]);
            }
            list.add(left+","+right);
            j++;
        }
        for(int j=0;j<list.size();j++){
            System.out.print(list.get(j)+' ');
        }

测试

软件测试笔试题
测试面试题
牛客测试面试题
maven

测试纸杯

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

怎么理解测试

验证系统的正确性,验证系统是否符合事先定义的要求,保证系统质量


测试用例设计书写规范

软件测试流程
测试目的/测试项/前置条件/输入数据/操作步骤/期望结果
测试环境/优先级/层次之间关联/是否适合自动化/预计耗时


为什么想做测试

自身性格适合+感兴趣


冒烟测试模糊测试

冒烟测试:在对系统进行正式测试之前,先验证主要功能是否实现,是否具备可测性
回归测试:修改代码之后确保没有引入新的错误,或导致其他代码产生错误
模糊测试:随机生成测试用例,一遍发现边缘错误
自动化测试:性能自动化和功能自动化
性能测试:负载测试、压力测试


什么项目适合自动化

项目需求变动不频繁(回归测试,每日构建后的测试验证)
项目周期长
自动化测试脚本可复用


黑盒测试/白盒测试

黑盒测试:数据驱动测试,检查系统功能是否按照需求规格说明书的规定正常使用
黑盒测试主要测到的错误类型有:不正确或遗漏的功能;接口、界面错误;性能错误;数据结构或外部数据访问错误;初始化或终止条件错误等等
常用的黑盒测试方法:等价类划分法;边界值分析法;因果图法;场景法;正交实验设计法;判定表驱动分析法;错误推测法;功能图分析法。
因果图法:条件桩列出问题的所有条件,动作桩列出针对问题采取的操作
条件项对条件具体赋值,动作项列出针对动作采取的措施
1)列出条件桩和动作桩
条件桩: a)驱动程序是否正确 b)是否有纸张 c)是否有墨粉
动作桩: a) 打印内容 b)提示驱动程序不对 c)提示没有纸张 d)提示没有墨粉
2)在这里插入图片描述
在这里插入图片描述
合并时首先找出相同的结果,查看判定表中相同结果的条件是否有执行动作与条件无关取值,将其合并。在这里插入图片描述在这里插入图片描述
正交实验法L8(2^7)7:因子数(正交表列数,影响因素,正交表列数)2:因子水平数(每个因素的取值) 8:测试次数(正交表行数)
白盒测试:结构化测试/逻辑驱动测试,知道其内部实现逻辑,按照程序内部的逻辑结构测试程序
静态测试&动态测试
静态测试是不用运行程序的测试,包括代码检查、静态结构分析、代码质量度量、文档测试等等,它可以由人工进行,充分发挥人的逻辑思维优势,也可以借助软件工具(Fxcop)自动进行。动态测试则需要执行代码,也是我们用的最多的一种测试,通过运行程序找到问题,包括功能确认与接口测试、覆盖率分析、性能分析、内存分析等。
在这里插入图片描述
语句覆盖:最弱覆盖
判定覆盖:设计测试用例,使得程序中每个判断的取真分支和取假分支至少经历一次,即每条路走一遍(M=T N=T/ M=F N=F)
条件覆盖:针对每个判断中的每一个逻辑都获得取真取假的可能
a>0 取真 T1 取假 F1
b>0取真 T2 取假 F2
a>1 取真T3 取假 F3
c>1 取真T4 取假F4
取值条件:T1 F2 T3 F4 / F1 T2 F3 T4(路线均为134)
并未保证代码覆盖,说明条件覆盖并不一定比判定覆盖好
判定条件覆盖:判定覆盖和条件覆盖设计方法的交集
取值条件:T1 T2 T3 T4(M=T,N=T)F1 F2 F3 F4(M=F,N=F)
条件组合覆盖:设计足够多的测试用例,使得判断中的每个条件的所有可能至少出现一次,并且每个判断本身的判定结果也至少出现一次 在这里插入图片描述
在这里插入图片描述
其实有四条路经,但是判定-条件覆盖只覆盖了其中三条路径,因此,即使是比较强的判定条件覆盖,也是不太充分的
基本路径覆盖
1、画出程序流程图
2、计算程序的圈复杂度
3、根据圈复杂度提取测试用例
4、设计测试用例
前提是知道有多少条路径
在这里插入图片描述
环路复杂性可以用V(G)表示
V(G)=区域数目【图的内部区域+外部区域①②③】
V(G)=边界数目-节点数目+2(6-5+2)
V(G)=判断节点数目+1(2+1)【A C】 在这里插入图片描述


W模型和V模型

添加链接描述
在这里插入图片描述
在这里插入图片描述

微信换头像,测试用例

在这里插入图片描述

微信发红包抢红包功能,设计测试用例

在这里插入图片描述
在这里插入图片描述


cpu占用率异常高,如何测试找到根源

1、看一下能否复现该场景
2、能的话可以使用性能监控工具grafana进行监控(cpu/内存/磁盘/网络情况等)
3、使用top查看进程使用资源情况
4、查询日志情况有无记录


微博评论测试

限制:120字以内
在这里插入图片描述


评论中图片不显示的原因
测试朋友圈点赞(功能、兼容性)

在这里插入图片描述

app加载慢为什么(从七层网络角度)
测试用例设计使用的方法,接口测试使用方法

黑盒测试:条件覆盖/判定覆盖/判定条件覆盖/条件组合覆盖/基本路径覆盖
白盒测试:等价类/边界值/判定表/正交实验法

京东购物车测试用例设计

在这里插入图片描述

如何测试小程序
有什么测试(功能性测试、安全性测试、压力测试)

单元测试:主要为白盒测试方法
集成测试:增量测试(自底向上或自顶向下)【桩模块和驱动模块概念】
系统测试:功能测试/非功能测试(易用性测试/安全性测试/性能测试/兼容性测试/可靠性测试/压力测试/容量测试/可恢复性/可维护性等)
安装测试

给你一个计算器app,怎么测试
如果你刷抖音闪退了,有哪些原因
如何为一个APP的网络通话功能设计测试用例?

答:正常情况通话、网络连接差时、多人通话、设备故障时通话。 (功能测试:电话能否拒接,能否挂断,响铃能否取消,能否设置为震动,我自己一直以为这是产品设计要管的事)

如何对视频播放软件卡顿的原因进行测试?

答:网络连接、服务器并发过高、客户端出错(答得比较宽泛),后来经面试官提示,又答设备性能问题(CPU和内存),视频编码解码异常。

上海用户反馈没有抖音刷新不出界面
什么是测试用例,编写测试用例的方法,加入刷今日头条时候,发现白屏,可能是什么原因?怎么测试?
如何模拟压力测试/如何模拟弱网环境
邮件测试

详解

app打开后空白页面,怎么测试出问题

在这里插入图片描述

和平精英对枪械测试

关于枪械的外观(是否可以被找到)
枪械的射程、子弹、后坐力、重量、

Selenium

1、selenium1.0工作原理 核心为RC
JavaScript代码可以很方便的获取页面上的任何元素并执行各种操作
同源政策:只有来自相同域名/端口/协议的JavaScript代码才能被浏览器执行
在这里插入图片描述
在这里插入图片描述

2、selenium2.0工作原理 核心为WebDriver

web service
Web Service 是解决应用程序之间相互通信的一项技术。严格的说,WebService是描述一系列操作的接口。它使用标准的、规范的XML 描述接口。【面向计算机】
测试面试题总结
selenium必备技能
接口测试介绍
接口测试扫盲
web安全测试
web功能测试
App自动化
App卡顿分析
Android基础入门全套知识
测试中cpu暴涨如何排查
接收邮件测试点
bug管理工具禅道的使用
TestNG使用教程
testng的xml配置
postman接口测试使用
软件测试必备知识
软件测试常用工具
测试常见面试题
功能测试常见面试题

智力题

智力题总结博客

  • 两根不均匀的香,各燃烧一个小时,怎么测出15分钟
  • 10000瓶水,其中一瓶有毒,小白鼠喝后12小时才会有反应,问在12小时内需要多少只小白鼠能知道哪瓶水有毒
  • 500个骨牌标了号数排在500个位置,每一次都拿走奇数位置的座位。最后剩的骨牌的号数是多少。

关于项目

如何保证项目质量
CI/CD什么含义,怎么实现,对于gitlab的CI/CD有什么可以改进的地方
关于selenium及自动化测试

关于selenium及自动化测试
####测试资料
测试资料下载

JMeter

JMeter
尝试去测试一个网站

selenium

selenium
1、selenium优势
模拟用户操作/不用额外设置cookie等/
2、工作原理
1.对于每一条Selenium脚本,一个http请求会被创建并且发送给浏览器的驱动
2.浏览器驱动中包含了一个HTTP Server,用来接收这些http请求
3.HTTP Server接收到请求后根据请求来具体操控对应的浏览器
4.浏览器执行具体的测试步骤
5.浏览器将步骤执行结果返回给HTTP Server
6.HTTP Server又将结果返回给Selenium的脚本,如果是错误的http代码我们就会在控制台看到对应的报错信息

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值