Java面试之基础知识(二)

有些话、适合烂在心里 提交于 2020-08-10 01:32:09

一、成员变量和局部变量

局部变量是定义在方法体内部的变量,其作用域在方法块内部有效。
局部变量在使用时,必须先初始化才能使用,否则不能顺理通过编译;
成员变量是定义的变了里那个,即属性,其作用域整个类中有效;
成员变量可以不指定初始值,系统有默认值:除了八种基本数据类型,
其他的引用类型都是null; Byte、Short、int的初始值为0;long的初始值为0L;boolean为false;
float的为0.0F;double的为0.0d;char为‘/u0000’;
被final修饰且没有被static修饰的成员变量必须显式赋初始值;

成员变量和局部变量的区别:
1、 public、protect、private、static可以修饰成员变量,但不能修饰局部变量;两者都可以用final修饰;
2、 成员变量存储在堆内存中,局部变量存储在栈内存中
3、 作用域不同,局部变量作用域在方法块内部,成员变量作用域整个类
4、 成员变量可以不设置默认值,局部变量必须设置默认值。

二、overload和override的区别

Overload(重载):
1、是同一个类中含有多个方法名称相同但参数列表(参数个数、类型、顺序)不相同的方法。
2、这些方法可能是当前类直接声明的,也可能是从父类中继承过来的可见方法。

Override(重写)
1、重写必须是子类继承父类后,子类重新声明了从父类中继承的可见(visible)方法,
2、此时,如果子类中重新声明的方法与父类中的方法 名称相同、参数列表完全相同、返回类型也一致,就说子类重写了父类中的同名方法。

相同点:都要求方法名称相同;

不同点: 1、 重载要求本类中有同名不同那个参的方法;重写必须发生在子类和父类之间; 2、 重载 要求同名方法的参数列表一定不相同;重写要求子类和父类的同名方法 的参数列表完全相同; 3、 重载对返回类型么有要求;而 重写 ( override ) 要求 被重写后的方法的返回 类型 必须跟 父类中相应方法保持一致 ( 如果是返回基本类型,则要求完全相同 ) 4、 重载( overload ) 对方法的 权限修饰符 没有要求,而 重写 ( override ) 要求 被重写后的方法的 权限修饰符 的范围不能缩小

注意:
子类不能重写父类的 静态方法,如果子类重新声明了父类中同名的静态方法, 则说 子类 隐藏 了 父类中的同名静态方法。

三、int 和 Integer 有什么区别?

int 是基本类型32位长度的整数

Integer 是类类型,是int的封装类

int和Integer之间可以通过自动装箱 自动拆箱 互相转换

String 和StringBuffer的区别?

String是immutable的,其内容一旦创建好之后,就不可以发生改变。 StringBuffer 是可以变长的,内容也可以发生改变 改变的原理是StringBuffer内部采用了字符数组存放数据,在需要增加长度的时候,创建新的数组,并且把原来的数据复制到新的数组这样的办法来实现。

说出ArrayList,Vector, LinkedList的存储性能和特性。
先说ArrayList和Vector
两者都继承了抽象类AbstractList,但是Vector是线程安全的,而ArrayList是非线程安全的

再说ArrayList和LinkedList的区别
ArrayList 是数组结构,所以定位很快,但是插入和删除很慢
LinkedList 是双向链表结构,所以插入和删除很快,但是定位很慢

Collection 和 Collections的区别

首先不要说成了一个是单数,一个是复数。。。
Collection是接口,是List和Set的父接口
Collections是工具类,提供了排序,混淆等等很多实用方法

HashMap和Hashtable的区别

HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式
区别1:
HashMap可以存放 null
Hashtable不能存放null
区别2:
HashMap不是线程安全的类
Hashtable是线程安全的类

final, finally, finalize的区别

final

  • final修饰类,方法,基本类型变量,引用的时候分别有不同的意思
  • 修饰类 表示该类不能被继承
  • 修饰方法 表示该方法不能被重写
  • 修饰基本类型变量 表示该变量只能被赋值一次
  • 修饰引用 表示该引用只有一次指向对象的机会

finally

  • finally 是用于异常处理的场面,无论是否有异常抛出,都会执行

finalize

  • finalize是Object的方法,所有类都继承了该方法。 当一个对象满足垃圾回收的条件,并且被回收的时候,其finalize()方法就会被调用

Error和Exception有什么区别

Error和Exception都实现了Throwable接口

Error指的是JVM层面的错误,比如内存不足OutOfMemoryError

Exception 指的是代码逻辑的异常,比如下标越界OutOfIndexException

abstract class和interface区别

使用方式:
抽象类只能够通过继承被使用
接口必须通过实现被使用

实现方法:
抽象类不仅可以提供抽象方法,也可以提供实现方法
接口只能提供抽象方法,不能提供实现方法。 但是在JAVA8版本开始,接口可以提供实现方法了,前提是要在方法前加一个default修饰符

heap和stack区别

heap:堆
stack:栈

存放的内容不一样:
heap: 是存放对象的
stack: 是存放基本类型(int, float, boolean 等等)、引用(对象地址)、方法调用

存取方式不一样:
heap: 是自动增加大小的,所以不需要指定大小,但是存取相对较慢
stack: 是固定大小的,并且是FILO 先入后出的顺序,并且存取速度比较快

数组与String

数组获取长度的手段是 .length 属性
String获取长度的手段是 length()方法
集合获取长度的手段是 size()方法
文件获取长度的手段是 length()方法

集合类

常见的集合
ArrayList,LinkedList,HashSet,HashMap,TreeSet 等等
常见方法:
size()
add()
remove()
等等

关键字:throws,throw,try,catch,finally

throws 用在方法声明上面,表示该方法有可能抛出某个异常
throw 抛出一个指定的异常
try catch 在try中有可能会抛出某个异常,一旦某个异常抛出后,就会在catch中进行捕捉,他俩一般说来都是成对出现的。
finally: 表示无论是否捕捉住异常,都会执行

java中会存在内存泄漏吗

因为Java是自动进行垃圾回收管理的,所以不存在 C语言中同等概念的内存泄漏,但是存在Java特色的内存泄漏

当某些对象不被使用,但是又有非直接引用指向的时候,那么就不满足垃圾回收的条件,而形成内存泄漏。

比如代码中的例子,每个Object创建的时候,有一个引用o指向,接着就被放进了集合al中。 下一个Object创建的时候,上一个Object就没有引用指向了。

java序列化

序列化指的是把一个Java对象,通过某种介质进行传输,比如Socket输入输出流,或者保存在一个文件里

实现java序列化的手段是让该类实现接口 Serializable,这个接口是一个标识性接口,没有任何方法,仅仅用于表示该类可以序列化。

多线程

多线程实现的三种方法

通常来讲,Java 创建一个线程有三种方式

  1. 继承一个Thread类
  2. 实现Runnable接口
  3. 匿名内部类

线程安全

线程安全,死锁

多线程的同步问题指的是多个线程同时修改一个数据的时候,可能导致的问题

多线程的问题,又叫Concurrency 问题

**synchronized 同步对象概念 **

Object someObject =new Object();
synchronized (someObject){
  //此处的代码只有占有了someObject后才可以执行
}

synchronized表示当前线程,独占 对象 someObject
当前线程独占 了对象someObject,如果有其他线程试图占有对象someObject,就会等待,直到当前线程释放对someObject的占用。 someObject 又叫同步对象,所有的对象,都可以作为同步对象
为了达到同步的效果,必须使用同一个同步对象

释放同步对象的方式: synchronized 块自然结束,或者有异常抛出

package multiplethread;
   
import java.awt.GradientPaint;
 
import charactor.Hero;
   
public class TestThread {
   
    public static void main(String[] args) {
 
        final Object someObject = new Object();
         
        final Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp = 10000;
          
        int n = 10000;
  
        Thread[] addThreads = new Thread[n];
        Thread[] reduceThreads = new Thread[n];
          
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(){
                public void run(){
                     
                    //任何线程要修改hp的值,必须先占用someObject
                    synchronized (someObject) {
                        gareen.recover();
                    }
                     
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };
            t.start();
            addThreads[i] = t;
              
        }
          
        for (int i = 0; i < n; i++) {
            Thread t = new Thread(){
                public void run(){
                    //任何线程要修改hp的值,必须先占用someObject
                    synchronized (someObject) {
                        gareen.hurt();
                    }
                     
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };
            t.start();
            reduceThreads[i] = t;
        }
          
        for (Thread t : addThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        for (Thread t : reduceThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
          
        System.out.printf("%d个增加线程和%d个减少线程结束后%n盖伦的血量是 %.0f%n", n,n,gareen.hp);
          
    }
       
}

sleep() 和 wait() 有什么区别

首先sleep和wait之间没有任何关系

sleep 是Thread类的方法,指的是当前线程暂停。

wait 是Object类的方法, 指的占用当前对象的线程临时释放对当前对象的占用,以使得其他线程有机会占用当前对象。 所以调用wait方法一定是在synchronized 中进行

说出数据连接池的工作机制是什么?

数据库连接池原理:
因为创建连接和关闭连接的行为是非常耗时的,会显著降低软件的性能表现。解决办法就是先创建n条数据库连接Connection,循环使用,但是不进行关闭,这样再执行SQL语句,就不需要额外创建连接了,直接使用现成的连接就可以了,从而节约了创建连接和关闭连接的时间开销。

简述synchronized和java.util.concurrent.locks.Lock的异同 ?

  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,Lock是代码层面的实现。

  2. Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。synchronized不行,会一根筋一直获取下去。 借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。

  3. synchronized在发生异常和同步块结束的时候,会自动释放锁。而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁。

多线程防止死锁,synchronized 发生异常或同步结束会自动释放锁,而lock需要手动释放锁。

Class.forName的作用?为什么要用?

Class.forName常见的场景是在数据库驱动初始化的时候调用。

Class.forName本身的意义是加载类到JVM中。 一旦一个类被加载到JVM中,它的静态属性就会被初始化,在初始化的过程中就会执行相关代码,从而达到"加载驱动的效果"

多线程应用场景

多线程最多的场景:web服务器本身;各种专用服务器(如游戏服务器);多线程的常见应用场景:

1、后台任务,例如:定时向大量(100w以上)的用户发送邮件;

2、异步处理,例如:发微博、记录日志等;

3、分布式计算

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!