牛客java刷题总结

一、基础语法与控制结构

1.取模%运算:计算时先忽略正负号,再根据被除数符号决定结果的符号,即结果符号和被除数符号一致

   12%-5=2,-12%5=-2,-12%-5=-2。

2.switch语句中的变量:

   jdk1.7之前可以是byte,short ,int ,char 及其包装类,jdk1.7之后加入String及枚举enum。

3.java中整型默认的是int,浮点默认的是double. 低类型可以赋给高类型的,反过来要强制转换。

double d=5.3e12;正确
float f=11.1; 错误 double类型的11.1 转成 float,是需要强制转换的,可以写成11.1f
int i=0.0; 错误 double类型的0.0 转成 int,也是需要强制转换的 
Double oD=3; 错误 int 无法转为封装类型Double,Double oD = 3.0可以, 会把double类型的3.0自动装箱为Double

4.Math.floor()   表示向下取整,返回double类型   (floor---地板) Math.floor(-4.2) = -5.0

   Math.ceil()   表示向上取整,返回double类型    (ceil---天花板)Math.ceil(5.6) = 6.0

   Math.round()  四舍五入,返回int类型      Math.round(-4.6) = -5

 

二、数组与字符串

1.java数组复制效率:

   System.arraycopy(native方法) > clone > Arrays.copyOf(底层调用System.arraycopy) > for循环逐个复制

2.String不可变,无论是s+=“aa”还是s=“aa”,不是在原对象上修改,而是创建了一个新对象然后再指向s,因此String是值传递

2.正则表达式

 

三、类与对象

1.抽象类中有构造函数,接口中无。jdk1.8之后,接口可以定义非抽象方法,default或static修饰的方法。

2.main方法也可以重载!!!

3.== 对象比较的是地址,基本数据类型比较的是值。Integer i=59;int j =59;Integer k=Integer.valueOf(59); i==j比较的是值(将包装类型i拆箱,再与j比较);i==k比较的是地址,在-127到128之间,i是从常量池中取值;在-127到128之间,Integer的valueOf方法也是从常量池中取值,这时i==k;当超过这个范围的时候,会在内存中new一个对象给k,这时i==k为false。String、Byte等包装类也有常量池。Integer m=new Integer(59),如果是new的对象,无论大小是否在-127到128之间,都不是从常量池中取值,所以m与i、k地址都不一样。

4.一个文件中可以有多个public class。外部类只能有一个public,但是内部类可以是public的。

5.初始化对象的执行顺序:父类静态域(静态变量/静态代码块) > 子类静态域 > 父类普通成员变量构造函数(要先将类中成员初始化)>构造块 > 父类构造方法 > 子类普通成员变量(要先将类中成员初始化)>构造块> 子类构造方法

6.hashcode()和equals()方法

两者都为Object中的方法,用来对比两个对象是否相等。

(以下情况都是未重写hahscode()和equals())

equals()相等的两个对象,它们的hashcode()肯定相等,也就是用equals()比较是绝对可靠的,但是它的效率低;

相同对象的hashcode()一定相等,但是hashcode相等的两个对象不一定是同一个对象(hash冲突),它们的equals不一定相等,也就是hashcode()不是绝对可靠的,但是它效率高;

 因此,每当需要对比时,先用hashcode()去对比,如果不一样,那么这两个对象肯定不相等,如果hashcode()相同,再去对比equals(),如果相等,则这两个对象一定相等。

 

四、集合

1.类图:

2.HashMap不能保证元素的顺序,LinkedHashMap可以保持数据的插入顺序,TreeMap可以按照键值进行排序(可自定比较器)。Hashtable是不允许key和value的值为空的,元素也是无序的,HashMap允许key和value的值为空的,这样的键只有一个,:源代码if(key = null) {putForNullKey(value);},因此不能用get()来判断集合中是否有key,要用containsKey().

Hashtable是线程安全的,HashMap是线程不安全的。

Hashtable直接使用对象的hashcode,而hashmap重新计算hash值。

hashtable初始数组默认大小为11,扩容为old*2+1,hashmap默认是16,且容量一定是2的指数。

hashtable和hashmap遍历都使用了Iterator,但是hashtable还是用了Enumeration的方式。


3.HashMap中采用是分离链表法(也叫链地址法)解决哈希冲突,在JDK1.6,JDK1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

Hashset 底层是调用 hashmap 来存储数据的:HashSet会将元素存储在HashMap的key集合中并通过这个来去重,然后为value赋一个static空对象PRESENT。

Thread、ThreadLocal和ThreadLocalMap源码分析

ThreadLocalMap是ThreadLocal的一个静态内部类,它使用开放定/地址法来处理散列冲突。ThreadLocalMap通过key(ThreadLocal类型)的hashcode来计算数组存储的索引位置i。如果i位置已经存储了对象,那么就往后挪一个位置依次类推,直到找到空的位置,再将对象存放。另外,在最后还需要判断一下当前的存储的对象个数是否已经超出了阈值(threshold的值)大小,如果超出了,需要重新扩充并将所有的对象重新计算位置。

4. 线程安全的map(阅读源码

(1)HashMap,TreeMap 未进行线程同步,是线程不安全的。 

(2)HashTableConcurrentHashMap 都是线程安全的。区别在于他们对加锁的范围不同,HashTable 对整张Hash表进行加锁(synchronized),而ConcurrentHashMap将Hash表分为16桶(segment),每次只对需要的桶进行加锁。 

(3)Collections 类提供了synchronizedXxx()方法,可以将指定的集合包装成线程同步的集合。如, 

List  list = Collections.synchronizedList(new ArrayList()); 

Set  set = Collections.synchronizedSet(new HashSet());

Map map = Collections.synchronizedMap(new HashMap());

 

五、IO

1.类图

2.序列化对象(序列化中的transient和static):具体的序列化由ObjectOutputStream和ObjectInputStream完成。transient修饰的变量不能被序列化(反序列化出来该对象的这个变量取默认值),static变量不管加没加transient都不可以被序列化(static修饰的变量是类变量,与对象无关,序列化是存储对象的状态,所以不能被序列化)。如果是在同一台机器上的同一个程序中,先序列化,再反序列化,该变量的值是有的,因为类变量会先被加载到内存中,反序列化出来时该变量是没有值的,但是内存中之前已经加载了类变量,类变量可以通过类名.变量取调用,也可以通过对象去调用,所以就会使用内存中之前加载的类变量,而不是默认值。如果换台机器或者换个程序,直接从文件中反序列化该对象,那么该变量则为默认值,因为类变量并没有没先加载到内存中去。

 

六、多线程

1.原子性:原子(atom)本意是“不能被进一步分割的最小粒子”,原子操作(atomic operation)意为"不可被中断的一个或一系列操作" 。原子操作就是不可再分的操作,即一件事要么做就做到底做完,要么就不做。在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制。由不可分性可知,原子性是拒绝多线程操作的(只有分解为多步操作,多个线程才能对其操作)。++ x ,  x ++这样的操作在多线程环境下是需要同步的。 因为这种语句包含三步:从内存中读x的值到寄存器中,对x寄存器加1,再把新值写回x所处的内存地址。 而对于x=y这样的操作,从内存中读取x值和y值都是原子操作的,但是还要把y赋值给x,这两个合起来就变成非原子操作了,因此也需要同步。 x=1只需将1写入内存一条指令,属于原子操作,不需要同步。同步是害怕在操作过程的时候被其他线程也进行读取操作,一旦是原子性的操作就不会发生这种情况。 因为一步到位的操作,其他线程不可能在中间干涉。

2.线程方法:

  • sleep()

在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。sleep()使当前线程进入阻塞状态,在指定时间内不会执行。 

  • wait()

在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待(让路给其他线程)。线程会释放掉它占有的锁,从而使别的线程有机会抢占该锁。 当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常wait()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。 

  • yield

暂停当前正在执行的线程对象。yield不会释放锁,只是让出CPU,从“运行状态”到“就绪状态”。

yield()只是使当前线程重新回到可执行(就绪)状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。 

yield()只能使同优先级或更高优先级的线程有执行的机会。  

  • join

等待该线程终止。 底层调用wait,也会释放掉锁

等待调用join方法的线程结束,再继续执行,会释放掉锁。如:t.join();//用于等待t线程运行结束,若无此句,main线程则会执行完毕,导致结果不可预测。

3.volatile

 

七、jvm

1.off-heap堆外内存

2. jvm规范大图

3.Java是解释型还是编译型语言?JIT、HotSpot Vm?

 

八、反射

 

 

九、异常

1.java提供了两种异常错误的异常类,分别为ERROREXCEPTION,错误和异常,他们的父类是Throwable. 

在这里插入图片描述

(1)error表示程序在运行期间出现了严重的错误,并且该错误是不可恢复的。(编译器不检查)
(2)Exception是程序本身可以处理恢复的异常,是编译器可以捕捉到的。分两类:运行时异常(runtime exception,也叫非检查异常uncheck)和非运行时异常(checked exception,也叫检查异常,编译异常)。


          运行时异常(非检查异常)包括:RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。

          非运行时异常(检查异常编译异常)包括:RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

 

2.throws和throw的区别:throws关键字是用在方法或类上,表示可能会发生指定的异常;而throw关键字表示抛出指定的异常,用在方法内。

3.假设利用return语句从try语句块中退出,在方法返回前,finally语句会被执行,如果finally中又return语句,则这个返回值会覆盖原始的返回值。

 

十、分布式

1.分布式服务器通信不可以使用管道,因为管道是半双工的,虽然可以双向,但是同一时间只能有一个方向传输,而分布式服务器需要双向通信。

 

十一、web

1.Servlet容器响应Web客户请求流程

HttpServlet容器响应Web客户请求流程如下: 

1)Web客户向Servlet容器发出Http请求; 

2)Servlet容器解析Web客户的Http请求; 

3)Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息; 

4)Servlet容器创建一个HttpResponse对象; 

5)Servlet容器调用HttpServlet的service方法,这个方法中会根据request的Method来判断具体是执行doGet还是doPost,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象; 

6)HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息; 

7)HttpServlet调用HttpResponse的有关方法,生成响应数据; 

8)Servlet容器把HttpServlet的响应结果传给Web客户。

 

2.servlet生命周期

3.servlet相关类结构

4.servlet在多线程下其本身并不是线程安全的:

如果在类中定义成员变量,而在service中根据不同的线程对该成员变量进行更改,那么在并发的时候就会引起错误。最好是在方法中,定义局部变量,而不是类变量或者对象的成员变量。由于方法中的局部变量是在栈中,彼此各自都拥有独立的运行空间而不会互相干扰,因此才做到线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值