秋招面试笔记

一级目录

二级目录

三级目录

一、测试知识

1、请就N95口罩设计测试用例

首先从功能方面考虑,测试口罩是否能够有效防止颗粒物和异常气体,防护病菌的过滤效果能否达到95%以上,能否过滤油性颗粒物。然后考虑口罩的界面:外包装是否有商品名,产地,合格证,口罩是否干净完好,是否有鼻夹,鼻夹能否弯折;易用性:是否容易佩戴,是否方便携带;性能:佩戴时间越长,其过滤效果是否降低,鼻夹是否易断;安全:口罩材质是否引起脸部过敏;兼容性:是否适合各种人群佩戴。

2、请结合自身对软件测试的理解,说一说作为一名测试人员,如何最大化的为团队创造价值

软件测试作为整个软件产品的质量把关环节,贯穿在整个软件产品的研发周期内。作为一名测试人员,应该在项目前期,跟进需求,充分理解功能需求;项目开发阶段,进行测试用例,测试数据准备,自动化准备;项目测试阶段,执行测试任务;如果有BUG存在,还需要进行回归测试。总之,在项目开始到结束的整个阶段,都要充分跟进项目进度,与开发人员,产品经理进行有效沟通保障产品的质量。

二、java基础知识

1、在java中,关于相对路径和绝对路径是这样解释的

绝对路径是指书写文件的完整路径,例如d:\java\Hello.java,该路径中包含文件的完整路径d:\java以及文件的全名Hello.java。使用该路径可以唯一的找到一个文件,不会产生歧义。但是使用绝对路径在表示文件时,受到的限制很大,且不能在不同的操作系统下运行,因为不同操作系统下绝对路径的表达形式存在不同。
相对路径是指书写文件的部分路径,例如\test\Hello.java,该路径中只包含文件的部分路径\test和文件的全名Hello.java,部分路径是指当前路径下的子路径,例如当前程序在d:\abc下运行,则该文件的完整路径就是d:\abc\test。使用这种形式,可以更加通用的代表文件的位置,使得文件路径产生一定的灵活性。

2、java的基本数据类型有八种

四种整数类型(byte、short、int、long):byte:8 位,用于表示最小数据单位,如文件中数据,-128~127 short:16 位,很少用,-32768 ~ 32767 int:32 位、最常用,-231-1~231 (21 亿) long:64 位、次常用。两种浮点数类型(float、double): float:32 位,后缀 F 或 f,1 位符号位,8 位指数,23 位有效尾数。 double:64 位,最常用一种字符类型(char): char:16 位,是整数类型。一种布尔类型**(boolean):true 真 和 false 假。

3、JAVA反射机制

在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制

4、java内存分区

内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
在这里插入图片描述
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,所有的对象实例以及数组都要在堆上分配。方法区与Java堆一样是所有线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量和即时编译器编译后的代码等数据。可以选择不实现垃圾收集。垃圾收集行为在这个区域是比较少出现的,但并不是数据进入该区就永久存在,这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。运行时常量池是方法区的一部分,用于存放编译期生成的各种字面常量和符号引用。Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法返回地址等信息。每一个方法从被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
在这里插入图片描述

局部变量表用于存放方法参数和方法内部定义的局部变量。本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
当我们创建一个对象(new Object)时,就会调用对象的构造函数来开辟空间,将对象数据存储到堆内存中,与此同时在栈内存中生成对应的引用,当我们在后续代码中调用的时候用的都是栈内存中的引用。还需注意的一点,基本数据类型是存储在栈内存中。

5、面向对象编程的三大特性

(1)继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继 承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增 加新的方法使之更适合特殊的需要。(2)封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。(3)多态性:多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。

6、Math类中提供了三个与取整有关的方法

分别是ceil、floor、round,这些方法的作用与它们的英文名称的含义相对应。
例如,ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.3)的结果是-11;floor的英文意义是地板,该方法就表示向下取整,Math.floor(11.6)的结果为11,Math.floor(-11.6)的结果是-12;最难掌握的是round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。

7、java.lang.NullPointerException出现的几种原因

(1)字符串变量未初始化
(2)接口类型的对象没有用具体的类初始化,比如:
Map map // 会报错
Map map = new Map(); //则不会报错了
(3)当一个对象的值为空时,你没有判断为空的情况。
(4)字符串与文字的比较,文字可以是一个字符串或Enum的元素,如下会出现异常
String str = null;
if(str.equals(“Test”)){//这里的代码将不会被触发,因为会抛出java.lang.NullPointerException异常。
}
(5)优先使用String.valueOf()方法代替toString()
当程序代码需要对象的字符串表示形式时,请避免使用该对象的toString方法。如果你的对象的引用等于null,NullPointerException则会抛出,使用静态String.valueOf方法,该方法不会抛出任何异常并打印"null"
(6)class被声明了类型, 默认 class = null; 这样在调用class中方法的时候系统只能给你个空指针异常, 给其实例化就好了:class = new Class();
(7)返回null,方法的返回值不要定义成为一般的类型,而是用数组。这样如果想要返回null的时候就能避免许多不必要的NullPointerException

8、equals与==的区别

==是判断两个变量或实例是不是指向同一个内存空间,equals是判断两个变量或实例所指向的内存空间的值是不是相同 ;
==是指对内存地址进行比较 , equals()是对字符串的内容进行比较;
==指引用是否相同, equals()指的是值是否相同。

9、java的深拷贝和浅拷贝

**浅拷贝:**对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
**深拷贝:**对基本数据类型进行值传递,对引用数据类型创建一个新的对象,并复制其内的成员变量,就是深拷贝。

10、对象和对象的引用

每个对象都是某个类(class)的一个实例(instance),操纵的标识符实际是指向一个对象的“引用”(reference)。

Person person;
person = new Person(“张三”);
在Java中new是用来在堆上创建对象用的,person是一个引用,是指向一个可以指向Person类的对象的引用。真正创建对象的语句是右边的new Person(“张三”);

Person person;
person = new Person(“张三”);
person = new Person(“李四”);
person先指向了“张三”这个对象,然后又指向了“李四”这个对象。也就是说,Person person,这句话只是声明了一个Person类的引用,它可以指向任何Person类的实例。一个引用可以指向多个对象,而一个对象也可以被多个引用所指。

Person person1 = new Person(“张三”);
Person person2 = person1;
person1和person2都指向了“张三”这个对象。

11、Integer.valueOf()和Integer.parseInt(),String的valueof方法

Integer inte3=Integer.valueOf(“654321”);//valueOf()可写数字,也可写字符串,将字符串转换成Integer.
static int parseInt(String s) 将字符串参数作为有符号的十进制整数进行分析。 static Integer valueOf(int i) 返回一个表示指定的 int 值的 Integer 实例。
static Integer valueOf(String s) 返回保持指定的 String 的值的 Integer 对象。
parseInt()返回的是基本类型intvalueOf()返回的是包装类Integer
Integer是可以使用对象方法的 而int类型就不能和Object类型进行互相转换
Integer.toString(int)方法:将int转换为String;
String的valueof方法是将各种类型转换成String,内部重载了不同类型转String的处理,所以推荐使用valueof方法。
String.valueof(int i/(long l/float f/double d)方法:将int/long/float/double类型转换成String类型
String.valueof(Object obj)方法:将对象转换成String类型
String.valueof(char data[])方法:将字符数组转换成String类型
String.valueof(char data[], int offset, int count)方法:将offset位置至offset+count位置的data[]数组转换成String类型

12、>>>,>>,/

为什么在 Java 中用 (low+high)>>>1 代替 (low+high)/2 或 (low+high)>>1 来计算平均值呢?好在哪里?

与>>是位运算符,只对整型有效(不能用于浮点型)。
当是整型的时候(low+high)>>1可以代替(low+high)/2。>>>是无符号右移运算符。如果 low+high是正整数,这三种运算是等价的。
由于有编译器优化,他们的效率应该是相同的(如果不存在编译器优化,移位运算更快)。用>>>一般是有特殊的目的

至于>>>和>>的区别,则在于有符号和无符号。比如-2>>>1的结果是2147483647,而-2>>1的结果是-1。(其中2147483647是-2的补码右移一位后,左边补0的结果。)-2 的二进制求法是正数取反加1,2 的二进制表示为0000 0000 0000 0000 0000 0000 0000 0010,因此**-2的二进制**表示为
1111 1111 1111 1111 1111 1111 1111 1110
无符号右移“ >>> ”) ,即无论正负数,将-2的二进制右移之后符号位均补 0 。
**(带符号右移“ >> ”)**将运算数的二进制整体右移指定位数,正数高位用0补齐,负数高位用1补齐(保持负数符号不变),所以-2 >> 1的二进制结果为 1111 1111 1111 1111 1111 1111 1111 1111 ,除符号位之外,减一取反,得到-2 >> 1的十进制数为 -1 。
这里计算平均值使用>>>取代>>,恐怕是因为可能出现很大的数字,这些数字单独用不会超过Integer.MAX_VALUE,但求和之后可能超过,这时如果使用>>或者/来计算,会因为溢出而算出负数结果。

三、计算机网络

1、session与cookie的区别

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

2、GET 和 POST

**共性:**它们都是 HTTP 请求协议的请求方法,而 HTTP 又是基于TCP/IP的关于数据如何在万维网中如何通信的协议,所以 GET/POST 实际上都是 TCP 链接。除了这 2 个请求方法之外,HTTP 还有 HEAD、PUT 、DELETE、TRACE、CONNECT、OPTIONS 这 6 个请求方法。GET 和 POST 的本质都是 TCP 链接,并无区别。但是由于 HTTP 的规定以及浏览器/服务器的限制,导致它们在应用过程中可能会有所不同。
**区别:**单纯获取资源的请求就规定为 GET、修改服务器资源的请求就规定为 POST;GET请求的数据会附在 URL 之后(放在请求行中),以 ? 分割 URL 和传输数据,多个参数用 & 连接。POST请求的请求参数是位于请求数据中;GET 方法只产生一个 TCP 数据包,浏览器会把请求头和请求数据一并发送出去,服务器响应 200 ok(返回数据);POST 方法会产生两个 TCP 数据包,浏览器会先将请求头发送给服务器,待服务器响应100 continue,浏览器再发送请求数据,服务器响应200 ok(返回数据)。这么看起来 GET 请求的传输会比 POST 快上一些(因为GET 方法只发送一个 TCP 数据包),但是实际上在网络良好的情况下它们的传输速度基本相同;GET 方法的安全性: 指的是非修改信息,即该操作用于获取信息而非修改信息。

四、数据结构与算法

1、HashMap的底层数据结构和实现原理

HashMap是基于数组来实现哈希表的,数组就好比内存储空间,数组的index就好比内存的地址;
HashMap的每个记录就是一个Entry<K, V>对象,数组中存储的就是这些对象;
HashMap的哈希函数 = 计算出hashCode + 计算出数组的index;
HashMap解决冲突:使用链地址法,每个Entry对象都有一个引用next来指向链表的下一个Entry;
HashMap的装填因子:默认为0.75;
map.put(k,v)实现原理
第一步:首先将k,v封装到Node对象当中(节点)
第二步:通过哈希算法计算出当前key的hash值
第三步:再通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。
如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。
如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末
尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
map.get(k)实现原理
第一步:先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
第二步:通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。
重点理解
如果这个位置上什么都没有,则返回null。
如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。
如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。
HashMap特点:
(1)无序:因为不一定挂到哪一个单向链表上的,因此加入顺序和取出也不一样。
(2)不可重复:怎么保持不可重复?
使用equals方法来保证HashMap集合key不可重复,如key重复来,value就会覆盖。存放在HashMap集合key部分的元素,其实就是存放在HashSet集合中,则HashSet集合也需要重写equals和hashCode方法。
hashmap集合的默认初始化容量为16,默认加载因子为0.75,也就是说这个默认加载因子是当hashMap集合底层数组的容量达到75%时,数组就开始扩容。
hashmap集合初始化容量是2的倍数,为了达到散列均匀,提高hashmap集合的存取效率。
(3)JDK8之后,如果哈希表单向链表中元素超过8个,那么单向链表这种数据结构会变成红黑树数据结构。当红黑树上的节点数量小于6个,会重新把红黑树变成单向链表数据结构,

2、HashMap和Hashtable的区别

HashMap是非synchronized,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行),而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。

3、LinkedHashMap和HashMap的区别

LinkedHashMap是比HashMap多了一个链表的结构。与HashMap相比LinkedHashMap维护的是一个具有双重链表的HashMap,LinkedHashMap支持2中排序一种是插入排序,一种是使用排序,最近使用的会移至尾部例如 M1 M2 M3 M4,使用M3后为 M1 M2 M4 M3了,LinkedHashMap输出时其元素是有顺序的,而HashMap输出时是随机的,如果Map映射比较复杂而又要求高效率的话,最好使用LinkedHashMap,但是多线程访问的话可能会造成不同步,所以要用Collections.synchronizedMap来包装一下,从而实现同步。另外,LinkedHashMap可以实现快速的查询第一个元素(First)跟结尾(Last)

4、二叉搜索树

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。

5、dfs(深度优先搜索)和bfs(宽度/广度优先搜索)

dfs(深度优先搜索)其实就是暴力把所有的路径都搜索出来,它运用了回溯,保存这次的位置,深入搜索,都搜索完了便回溯回来,搜下一个位置,直到把所有最深位置都搜一遍,要注意的一点是,搜索的时候有记录走过的位置,标记完后可能要改回来;使用递归。
bfs(宽度/广度优先搜索),这个一直理解了思想,不会用,后面才会的,思想,从某点开始,走四面可以走的路,然后在从这些路,在找可以走的路,直到最先找到符合条件的,这个运用需要用到队列(queue),需要稍微掌握这个才能用bfs。

6、集合和队列,栈

在这里插入图片描述
在这里插入图片描述
Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接 口
Queue queue=new LinkedList();
queue.offer(“a”);//添加元素,返回false
queue.poll();//返回第一个元素,并在队列中删除,返回null
queue.peek(); //返回第一个元素,返回null
queue.add(“a”);//添加元素,抛异常
queue.poll();//返回第一个元素,并在队列中删除,抛异常
queue.element();//返回第一个元素,抛异常
是Vector的一个子类,它实现了一个标准的后进先出的栈。Stack来自于Vector,那么显然stack的底层实现是数组。
push( num) //入栈
pop() //栈顶元素出栈
empty() //判定栈是否为空
peek() //获取栈顶元素
search(num) //判端元素num是否在栈中,如果在返回1,不在返回-1。

8、哈希表和HashMap

在对象的存储位置和对象的关键属性(设为 k)之间建立一个特定的对应关系(设为 f),使每个关键字和结构中一个唯一的存储位置相对应。如果此对象在集合中,则必定在存储位置 f(k)上,因此不需要与集合中的其他元素进行比较。称这种对应关系 f 为哈希(Hash)函数,按照这种思想建立的表为哈希表
哈希(Hash)函数:对不同的关键字可能得到同一哈希地址,即哈希冲突,冲突只能尽量地少,而不能完全避免。因为,哈希函数是从关键字集合到地址集合的映像。而通常关键字集合比较大,它的元素包括所有可能的关键字,而地址集合的元素仅为哈希表中的地址值。因此,在实现哈希表这种数据结构的时候不仅要设定一个“好”的哈希函数,而且要设定一种**处理冲突的方法。
哈希表:根据设定的Hash函数 - 和处理冲突的方法,将一组关键字映象 到一个有限的连续的地址集(区间)上,并以关键字在地址集中的象 作为记录在表中的存储位置,这样的表便称为Hash表 ;
**处理冲突方法:**开放定址法,再哈希法(在同义词产生地址冲突时计算另一个哈希函数地址,直到冲突不再发生),链地址法(在Hash 出来的哈希地址中不直接存Key ,而是存储一个Key 的链表 ,当发生冲突 时,将同义的Key 加入链表 ;)公共溢出区(建立一个公共溢出区,用来存放有冲突的Key 。比如设立另一个哈希表,专门用来存放出现冲突的同义词。)
Hash表 是一种逻辑数据结构,HashMap是Java中的一种数据类型(结构类型)。HashMap实现了哈希表这种数据结构,但它绝不是哈希表本身。

9、Collections.sort和Arrays.sort分析比较

Collections.sort:
@SuppressWarnings("unchecked")
    public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
    }
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> void sort(List<T> list, Comparator<? super T> c) {
        list.sort(c);
    }

由jdk源码我们可知,Collections类中的sort函数提供了两种重载。
第一种方法:
<T extends Comparable<? super T>>该方法的泛型必须实现了Comparable中的compareTo()方法,输入为相应的List。
第二种方法:
对泛型不作要求,但是输入参数中需提供Comparator
我们可以通过例子来分析:

public class run {
    
    public static void main(String[] args) throws Exception 
    {
        //类A实现了Comparable接口,所以可以直接使用Collections.sort()方法
        A a1 = new A(3);
        A a2 = new A(1);
        A a3 = new A(2);
        ArrayList<A> lista = new ArrayList<A>();
        lista.add(a1);
        lista.add(a2);
        lista.add(a3);
        Collections.sort(lista);
        for(A a : lista)
            System.out.println(a.value);//输出123
        
        //类B没有实现Comparable接口,通过自定义的比较器Comparator来使用Collections.sort()方法
        ArrayList<B> listb = new ArrayList<B>();
        B b1 = new B(5);
        B b2 = new B(7);
        B b3 = new B(6);
        listb.add(b1);
        listb.add(b2);
        listb.add(b3);
        BComparator comparator = new BComparator();
        Collection.sort(listb);//错误,会报错,缺少比较器
        Collections.sort(listb, comparator);//正确
        for(B b : listb)
            System.out.print(b.value);//输出567
    }

}
//类A实现了Comparble接口
class A implements Comparable<A> {
    public int value;
    //升序排列
    @Override
    public int compareTo(A o) {
        if(this.value > o.value)
            return 1;
        else if(this.value < o.value)
            return -1;
        else
            return 0;
    }
    public A (int i) {
        value = i;
    }
}
//类B是一个普通类
class B {
    public int value;
    public B(int i) {
        value = i;
    }
}
//定义比较器,实现了Comparator接口,可以用来作为输入参数定义比较方法
class BComparator implements Comparator<B> {
    //升序排列
    @Override
    public int compare(B o1, B o2) {
        if(o1.value > o2.value)
            return 1;
        else if(o1.value < o2.value)
            return -1;
        else
            return 0;
    }
    
}

Arrays.sort
由jdk源码可知,Arrays.sort方法也可以通过自定义Comparator的方法来实现自定义排序。

public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }

    /** To be removed in a future release. */
    private static <T> void legacyMergeSort(T[] a, Comparator<? super T> c) {
        T[] aux = a.clone();
        if (c==null)
            mergeSort(aux, a, 0, a.length, 0);
        else
            mergeSort(aux, a, 0, a.length, 0, c);
    }
我们继续通过例子来分析:

public class run {
    
    public static void main(String[] args) throws Exception 
    {
        A a1 = new A(5);
        A a2 = new A(7);
        A a3 = new A(6);
        A[] array2 = new A[3];
        array2[0] = a1;
        array2[1] = a2;
        array2[2] = a3;
        Arrays.sort(array2);
        for(int i = 0; i < array2.length; i++) {
            System.out.print(array2[i].value);//输出567
        } 
        
        B b1 = new B(5);
        B b2 = new B(7);
        B b3 = new B(6);
        B[] array = new B[3];
        array[0] = b1;
        array[1] = b2;
        array[2] = b3;
        BComparator comparator = new BComparator();
        Arrays.sort(array);//错误,B没有实现Comparable接口,程序无法判断排序标准
        Arrays.sort(array, comparator);//正确
        for(int i = 0; i < array.length; i++) {
            System.out.print(array[i].value);//输出567
        }
    }
}
//类A实现了Comparble接口
class A implements Comparable<A> {
    public int value;
    //升序排列
    @Override
    public int compareTo(A o) {
        if(this.value > o.value)
            return 1;
        else if(this.value < o.value)
            return -1;
        else
            return 0;
    }
    public A (int i) {
        value = i;
    }
}
//类B是一个普通类
class B {
    public int value;
    public B(int i) {
        value = i;
    }
}
//定义比较器,实现了Comparator接口,可以用来作为输入参数定义比较方法
class BComparator implements Comparator<B> {
    //升序排列
    @Override
    public int compare(B o1, B o2) {
        if(o1.value > o2.value)
            return 1;
        else if(o1.value < o2.value)
            return -1;
        else
            return 0;
    }
}

总结
由上可知,Arrays.sort和Collections.sort实现自定义排序时基本相同,分为两种:
1)被排序的元素自身实现了Comparable接口
2)被排序的元素没有实现Comparable接口,自定义比较器Comparator类作为输入参数输入
区别:
Collection.sort是给List进行排序,而Arrays.sort是给数组进行排序。

五、操作系统

1 、线程同步机制

临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphor在这里插入代码片e)、事件(Event)的区别
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。
2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享
3、**信号量:**它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作。在同一个进程的多线程同步锁,宜用临界区锁,它比较节约线程上下文切换带来的系统开销。但因临界区工作在用户模式下,所以不能对不同进程中的多线程进行同步。因互斥对象锁属于内核对象,所以在进行多线程同步时速度会比较慢,但是可以在不同进程的多个线程之间进行同步。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值