java面试知识点总结

(持续更新中:有错请指出)

java基础

面向对象的三大特征

继承,封装,多态

  • 继承:属于is-a关系,子类继承父类,为了代码重写,提现不同抽象层次,子类继承了父类的成员变量和方法,私有属性和方法也被继承,只不过无法访问
  • 封装:封装隐藏了类的内部实习,保护数据,需要调用get, set方法修改和访问数据
  • 多态:同一对象在不同时刻的不同表现
    实现多态必须满足三个条件:继承,重写,向上转型

多态详解

String,StringBuffer,StringBuilder区别

String:是不可变类,底层代码:一个由final修饰的不可变字符数组

//声明为private 和 final
    private final char value[];

StringBuffer和StringBuilder:可变字符数组。都是都继承AbstractStringBuilder类,而AbstractStringBuilder中的申明的是可变字符数组,

//无final修饰
   char[] value;

StringBuffer线程安全因为底层方法都有synchronized加锁,但是效率低,而StringBuilder线程非安全,但是效率高

String str=new String(“abc”)创建了多少对象

答案:可能1个可能2个。
首先看String str=“abc”,这行代码创建对象时候首先会在String常量池中找是否有abc字符串,有则直接取出来,没有则创建abc对象到常量池。
String str=new String(“abc”)一样先看常量池中有没有,没有则在常量池中创建abc对象,然后通过new String(“abc”)又在堆空间创建一个对象,一共2个,如果常量池有则只需在堆中创建对象,一个1个。

String常用方法

String 类的常用方法都有那些?

indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。

equals和==区别

==:

  • 基本类型:比较值是否相等

  • 引用类型:比较引用地址是否相等

equals:
本质是Object类的方法,本质上就是==,只不过一般String,Integer类重写了equals方法,使之比较的是String里面的一个一个内容

//Object类中的equals方法
    public boolean equals(Object obj) {
        return (this == obj);
    }
//Stringlei中重写的equals方法,比较的是里面的内容
 public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

重载和重写

  • 重载:编译时多态
    指同一个类中运行多个同名的方法存在,但是参数类型不一样,重载的区别也只在于参数的不同(参数类型,参数个数)

  • 重写:运行时多态
    指子类继承父类,对父类的相同方法进行覆盖,且子类的访问修饰符必须大于父类,抛出异常必须小于父类

接口和抽象类区别

从设计层面讲:抽象类是对类的抽象(比如person类中可以对person定义公共的方法等待具体的类去实现),而接口是行为的抽象,需要一个类去实现这个接口中的方法

抽象类:

  1. 抽象的方法和抽象的类必须由关键字abstract修饰,一个类有抽象方法,它一定的抽象类,而抽象类不一定必须有抽象方法。
  2. 抽象类中的抽象只定义方法声明,不具有实现。
  3. 抽象类不可以被创建对象(抽象类和接口都能创建对象,只是后边的new不能是new他们自己,但可以new他们的实现类)。
  4. abstract类中定义abstract方法必须在子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
  5. 抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰

接口:

  1. 接口中的成员变量默认都是public,static,final类型的,必须被显示的初始化。
  2. 成员方法默认都是public ,abstract 类型的。只要方法声明(jdk1.8新增函数式接口可以实现多个static方法和default方法)
  3. 接口不能实例化,因为接口是完全抽象的,要创建对象则需要new一个实现类和上面抽象类一样(也就是向上转型)
  4. 当一个类实现了某个接口时,必须实现接口中的所有抽象方法,否则这个类必须声明为抽象的。
  5. 接口只能用extends继承其他接口,不能使用implements去实现
  6. 接口可以嵌套在类或者其他接口中。

想了解抽象类和接口更多知识的朋友这里点

final修饰符号

final被称为最终类
修饰类:不能被继承
修饰方法:不能被重写
修饰成员变量:

  • 基本类型:代表此变量代表的数值不能改变
  • 引用类型:代表该变量引用不可变,但是所指向的内容可以变

修饰参数:代表参数不可变

详情参照:
https://blog.csdn.net/qq_44819486/article/details/110276355

final finally finalize区别

  • final:被称为最终类,可以修饰类,方法,变量,参数。
  • finally:通常作用与try-catch代码块中,代表一定会执行finally中的类容,通常在return之前会执行finally中的类容,
  • finalize:属于Object中的方法,一般由GC(垃圾回收器)调用,是一个对象是否能回收的判断

static修饰符号

static修饰符可以修饰变量和方法,或者声明一个代码块

  • 修饰方法和变量:会变成类方法和类变量,在类进行加载的时候进行初始化
  • 修饰代码块:代码块里面的类容会在类加载的时候进行初始执行一次
    注意:1:静态只能访问静态,非静态可以方法静态或非静态。2:可以不创建对象直接访问静态资源

static代码块,普通代码块访问顺序

在类加载的时候首先初始化static修饰的变量方法代码块,当new对象的时候会调用构造方法,而普通代码块则在调用构造方法之前执行

哪些方法属性Object类

在这里插入图片描述 1.clone方法

  protected native Object clone() throws CloneNotSupportedException;

保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

2.getClass方法

 public final native Class<?> getClass();

final方法,获得运行时类型。

3.toString方法

   public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

返回类的名字@实例的哈希码的16进制的字符串。建议自己写类的时候都重写此方法

4.finalize方法

   protected void finalize() throws Throwable { }

实例被GC垃圾回收器回收的时候触发的操作

5.equals方法

    public boolean equals(Object obj) {
        return (this == obj);
    }

在Object类中可见equals底层就是==,但一般常用方法都重写了equals方法

6.hashCode方法

    public native int hashCode();
  • 该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
  • equals和hashcode方法满足的关系:两个对象hashcode不相等则equals一定不相等,hashcode相等equals可能相等

7.wait方法

    public final void wait() throws InterruptedException {
        wait(0);
    }

native方法,并且不能重写。暂停线程的执行。wait()方法会释放锁 。timeout是等待时间,会进入睡眠,直到其他线程调用该对象的notify()/notifyAll方法,或者timeout到了,或者其他线程调用了interrupt中断该线程。此时线程则可以被调度。
如果是被中断的话就抛出一个InterruptedException异常。
8.notify方法

   public final native void notify();

该方法唤醒在该对象上等待的某个线程。
9.notifyAll方法

    public final native void notifyAll();

该方法唤醒在该对象上等待的所有线程

运算符号

  • 逻辑运算符号:
    &&(与):两者为true才为true,否则为false;
    ||(或):两者为false才为false,否则为true;

  • 按位运算符号:
    &(与):转换为2进制;1&1=1;1&0=0;0|1=0;0&0=0
    |(或):转换为2进制;1|1=1;1|0=1;0|1=1;0|0=0

  • 异或运算符号
    ^:相同则为0,否则为1
    1^1=0
    1^0=1
    0^1=1
    0^0=0

Math.round(-1.5) 等于多少?

Math.round(-1.5)=-1
Math.round(1.5)=2

>>,<<,>>>区别

<<      :     左移运算符,num << 1,相当于num乘以2
	注意:负数先换算成补码,让后位移,再求源码

>>      :     右移运算符,num >> 1,相当于num除以2
	注意:负数先换算成补码,让后位移,再求源码
	
>>>    :     无符号右移,忽略符号位,空位都以0补齐

具体举例可以看下面这篇文章:
<<,>>,>>举例说明

java只有值传递

首先看这样一个例子

class TestIt
{
    public static void main ( String[] args )
    {
        int[] myArray = {1, 2, 3, 4, 5};
        ChangeIt.doIt( myArray );
        for(int j=0; j<myArray.length; j++)
            System.out.print( myArray[j] + " " );
    }
}
class ChangeIt
{
    static void doIt( int[] z ) 
    {
        z = null ;
    }
}

答案是12345,为什么?
首先程序会找到main方法去执行,初始化myArray之后,这里注意myArray引用变量存储在栈中存储的是地址,指向堆中的实际数组对象,然后调用ChangeIt.doIt( myArray ),这时myArray传过去的是栈空间的引用地址,指向同一个对象,然后z=null;这里只是将z指向的地址赋值为null,而myAraay还是指向本来的对象,所以值没有改变
再看下面代码:

class ChangeIt
{
    static void doIt( int[] z )
    {
        int[] A = z;
        A[0] = 99;
    }
}
 
class TestIt
{
    public static void main( String[] args )
    {
        int[] myArray = {1, 2, 3, 4, 5};
        ChangeIt.doIt(myArray);
        for (int i = 0; i < myArray.length; i++)
        {
            System.out.print(myArray[i] + " ");
        }
    }
}

这时候答案为99,2,3,4,5
这时候传过来的myArray把引用地址传给了z,z再传给了A,相当于A,z,myArray都指向同一个对象,A[0]再堆中的值被改变,则这时候myArray指向的对象被改变
总结:java语言实际上是值传递

容器

说说ArrayList和LinkedList区别

  1. 底层实现:ArrayList基于数组,LinkedList基于双向链表
  2. 读写速度:ArrayList查询快,增删改慢,LinkedList查询慢,增删改快
  3. 默认容量:ArrayList默认容量10,LinkedList无
  4. 占用资源:LinkedList底层多维护next,pre结点开销更大

感兴趣他们为什么是这样的伙伴可以看看底层源码
从底层认知ArrayList
从底层认知LinkeList

说说hashmap,hashtable区别

  • 线程性:hashmap线程非安全,hashtable线程安全(hashtable使用synchronized锁方法)
  • 值:hashmap允许key-value为空,hashtable不允许
  • 底层实现:hashmap(数组+链表+红黑树),hashtable(数组+链表)
  • 容量:hashmap默认16,且扩容2倍,且容量一定是2^n。hashtable默认容量11,扩容为2倍+1,不一定是2的n次方

说说HashMap,LinkedHashMap,TreeMap区别

区别:

  • HashMap:HashMap中k的值没有顺序,在遍历的时候会比LinkedHashMap快,例外:当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢。因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
    用途:常用来做统计。
  • LinkedHashMap:继承hashmap,多维护了双向链表,比hashmap多耗费资源,但是允许查看插入顺序,和访问顺序,顺序由boolean变量accessOrder决定,
    用途:可以实现简单的LRU缓存策略
  • TreeMap:TreeMap底层是红黑树,并且它是按照 key 的指定排列,具体的顺序由指定的Comparator来决定,
    用途:当需要按照key值的特点顺序的特点的时候可以实现Treemap

相同点:

  • LinkedHashMap、HashMap、TreeMap都实现了Map接口,使用键值对的形式存储数据和操作数据。
  • 都是线程非安全

有兴趣底层源码的伙伴可以看看一下文章:
HashMap
TreeMap
LinkedHashMap

为什么建议使用ConcurrentHashmap不使用hashtable

  1. ConcurrentHashmap锁的是Node对象,且读操作不上锁,而hashtable锁的是方法,相当与整个hashtable,所以hashtable每次同步执行的时候都要锁住整个结构
  2. hashtable继承Dictionary类,而Dictionary类基本被遗弃

可以看一下源码
ConcurrentHashmap

   final V putVal(K key, V value, boolean onlyIfAbsent) {
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

可以看出 synchronized (f) 锁的是node对象
看HashTable:

   public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

很清晰的看出HashTable锁的是方法,而ConcurrentHashmap很明显锁的粒度更细粒

说说线程安全类容器

  • map
    HashTable(synchronized锁的方法),ConcurrentHashMap(synchronized锁的node对象)
  • collection
    voctor以及下面图示方法
    在这里插入图片描述 比如使用ArrayList,LinkedList的时候需要同步则需要使用synchronizedList
List l=Collections.synchronizedList(new ArrayList<...>());
List l=Collections.synchronizedList(new LinkedList<...>());

set同上。
但是
这里很多小伙伴都发现了,ConcurrentHashMap我知道啊,但是这SynchronizedMap是啥啊!这里LZ暂时还没怎么研究过SynchronizedMap,不过在多线程的时候需要hashmap的时候肯定建议使用ConcurrentHashMap

并发

synchronized的作用范围

synchronized修饰代码块:只作用于同一个对象,如果不同对象调用该代码块就不会同步。

synchronized修饰实例方法:和同步代码块一样,只作用于同一个对象。

synchronized修饰静态方法:作用于整个类,其锁就是当前类的class对象锁。

synchronized修饰类:作用于整个类,两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。

在高IO(IO密集型)的情况下怎么设计线程

什么叫高IO?
cpu使用率较低,会大量使用i/o操作,导致cpu空闲,
怎么解决?
开两倍cpu核数的线程,就是为了提升cpu利用率,
那什么叫cpu密集型,怎么解决?
cpu密集型指的就是cpu利用率较高(程序经常做一些复杂运算)。一般线程就开到等于cpu核数就可以了,开多了导致频繁上下文切换,效率降低

IO

IO流分哪为几种

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

计算机网络

OISI和TCP/IP协议有哪几层

在这里插入图片描述OSI是理想模型,实质上我们用的比较多是5层体系结构从下到上:

  • 物理层
  • 数据链路层
  • 网络层
  • 运输层
  • 应用层

get,post区别

get,post本质就是http协议的两种请求,而http协议就是基于tcp/ip协议
所以get,post两种都基于tcp/ip请求
而它们耳熟能详的特点如下:

  • get请求会被浏览器主动cache,而post不会(手动设置)
  • get请求主要是获取数据,post请求主要修改数据
  • get请求没有post请求安全,因为get请求明文传输,参数会暴露在url上,而post请求不会
  • get请求在url中传输的参数有长度限制,而post没有
  • get请求只接受ASCLL编码,而post没有限制
  • get请求的参数会保存到浏览器记录中,post不会
  • get产生1个tcp数据包,而post产生2个tcp数据包

注意:get,post本质上是没有啥区别的,比如上面说的get有长度限制,post没有,实质上get,post都没有长度限制,而是因为,许多浏览器都对get做出了限制,限制了get传输的长度

http和https区别

  • HTTP:超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法。
  • HTTPS:《图解HTTP》这本书中曾提过HTTPS是身披SSL外壳的HTTP。HTTPS是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。
    具体参考:
    https://blog.csdn.net/xiaoming100001/article/details/81109617

http常见的状态码

  • 1××:信息性状态码
  • 2××:成功状态码
  • 3××:重定向状态码
  • 4××:客户端错误状态码
  • 5××:服务器错误状态码

200: OK 服务器成功处理了请求(这个是我们见到最多的)

204: No Content 请求成功处理,没有实体的主体返回

206 :Partial Content GET范围请求已成功处理

301/302 Moved Permanently(重定向)请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置

303 See Other 临时重定向,期望使用GET定向获取

304 Not Modified 发送的附带条件请求未满足

307 Temporary Redirect 临时重定向,POST不会变成GET

400 Bad Request 请求报文语法错误或参数错误

401 Unauthorized 需要通过HTTP认证,或认证失败

403 Forbidden 请求资源被拒绝

404 Not Found(页面丢失)未找到资源

500 Internal Server Error 服务器故障或Web应用故障

501 Internal Server Error服务器遇到一个错误,使其无法对请求提供服务

503 Service Unavailable 服务器超负载或停机维护

说一说TCP/UDP在应用层中的协议

  • TCP
    HTTP:超文本传输协议(www服务)80端口
    HTTPS:安全的HTTP协议 443端口 
    FTP:文件传输协议 21端口  
    SMTP:简单邮件传输协议(发送邮件) 25端口
    POP3:第三版邮局协议(接收邮件) 110端口 
    TELNET:远程登录协议 23端口

  • UDP
    TFTP:简化的文件传输协议  69端口
    DNS:域名解析协议 53端口
    DHCP:动态主机配置协议  67端口
    NTP:网络时间协议 23端口
    SNMP: 简单网络管理协议  161端口

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值