Java基础面试题

Java开发进阶 专栏收录该内容
65 篇文章 0 订阅
Java基础面试题
##### 1.简述JDK,JRE,JVM的关系:
JDK:java development kit: Java开发工具包,包括了JRE,提供有一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)
JRE:Java runtime environment:java运行时环境,包括有Java的JVM,runtime class libraries和Java application launcher,这些是运行Java程序的必要组件。
JVM:java virtual machine:就是我们常说的java虚拟机,它是整个java实现跨平台的最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。
  也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
  只有JVM还不能成class的执行,因为在解释class的时候JVM需要调用解释所需要的类库lib,而jre包含lib类库。
  JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
  ######## 图解:   ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190527083524755.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI1OTA1MTU5,size_16,color_FFFFFF,t_70)    ##### 2.main方法能否声明为private
根据Java语言规范,main方法必须声明public(可参考Java语言规范的官方文档),不过,当main方法不是public时,某些版本的Java解释器也可以编译通过,在[Java Bug库](http://bugs.java.com/bugdatabase/index.jsp) 中输入bug号4252539可以查看,这个bug被标明“关闭,不予修复”,sun公司工程师解释说:“Java虚拟机规范工并没有强制要求mian方法为public,并且“修复这个bug有可能带来其他的隐患”,不过,在Java SE 1.4版本后强制mian方法为public
3.&和&&的区别:
4.不通过中间变量交换两个变量的值
class one{
 int a=10;
 int b=20;
 a=a+b;//a=30
 b=a-b;//b=10
 a=a-b;//a=20
}

class two{
 int a=10;
 int b=20;
 a=a*b;//a=200
 b=a/b;//b=10
 a=a/b;//a=20
}

class three{
 int a=10;
 int b=20;
 a=b+(b=a)*0;
}
5.Java中跳出当前的多重循环

break? 只能跳出当前层循环
跳出多重循环要label/flag标签;

class Test{
 public static void main(String[] args){
  mark://标签,下面break mark
  for(int i=0;i<10;i++){
   for(int j=0;j<10;j++){
    if(i==j){
     break;//只能跳出当前层
     break mark;//跳出到标记层
    }
   }
  }
 }
}
6.String s=new String(“xyz”)创建了几个String Object;

创建了可以是一个,可以是两个对象,先创建了个常量,又在栈中创建了引用指向堆中地址为xx01的对象

class Test{
 public static void main(String[] args){ //入栈,main方法中的所有代码同处一个内存空间
  String s1=new String("xyz")//创建了2个对象
  String s2="xyz";//创建了0个字符串对象,指向了s1创建的常量对象
  String s3=s2;//创建了0个字符串对象,s3指向了s2,最终指向s1的常量对象
  String s4=new String("xyz");//创建了1个,因为常量池中已有xyz

  System,out.print(s2=="xyz");//return true
  System,out.print(s1==s4);//return false 指向不同堆地址
  System,out.print(s1==s2);//return false s1在堆中创建对象指向常量池而s2直接指向常量池
  System,out.print(s1=="xyz");//return false 与上题一致
 }
}

在这里插入图片描述

7.hashCode和equals的关系

重写过hashCode和equals吗?
为什么重写equals时必须重写hashCode?

hash是散列的意思,即把任意长度的输入,通过散列算法转换成固定长度的输出,该输出就是散列值
1.不同关键字经过散列算法变换后可能会得到同一个散列地址,这种的称为Hash碰撞
2.如果两个Hash值不同,(前提是同一个Hash算法)那么这两个Hash值对应的原始输入必定不同
HashCode()的作用:为了获取哈希散列码,返回int型整数
1.HashCode的存在主要是为了提高在散列结构中查找的效率,在线性表中没有作用 
     在HashMap中put里调用了hashCode算法,那么get肯定也有,int比对肯定比String比对要快。即先用hash懂得确定一个范围。
2.两个相等对象分别调用equals时都会返回true
3.如果两个对象equals()  return true,则HashCode一定相同
4.两个对象Hash Code相同,不代表两个对象相同,只能说明这两个对象在散列存储结构中处于相同的位置
5.因此equals方法被覆盖,则HashCode方法也必须被覆盖
6.hashCode的默认行为是在堆中的对象产生独特值
7.Integer的HashCode是它自身
8.求最大公约数和最小公倍数
class Test{
 public static void main(String[] args){
  int a=10;
  int b=20;
  int min=a>b?b:a;
  int result=0;
  for(int i=min;i>1;i--){
   if(a%i==0 && b%2==0){
    result=i;
    break;
   }
  }
  System.out.print(result);
 }
}

面向对象方面:

1.Java和C++的区别

都是面向对象,但C++提供指针使程序员可以直接操作内存,同时也要手动进行垃圾回收,C++可以多继承。
2.简述面向对象的特征
封装:将对象的属性和方法隐藏,提供外部访问接口,保证安全性
继承:在原有类的基础上创建新的类,不改变原有的类,对原有类进行重写或扩展,实现更复杂的功能。提高程序的可扩展性。
多态:程序运行过程中不确定的状态,在实际编码中,我们将子类的引用指向父类,在传入参数时我们传的是父类的引用,在执行是才确定这个引用会指向哪个类的实例。在编译时不确定。

2.重载和重写

重载:在一个类中,不存在继承关系,是编译时多态,方法名相同,参数列表不同,方法返回值和访问修饰符可以不同,但我们不能根据返回值相同与否来判断重载。因为无法确定上下文的调用,不知道调用的哪一个。除非编译器能够有判断上下文调用的机制
重写:存在继承关系,方法名,参数列表必须相同,返回值范围和抛出异常范围小于等于父类,访问修饰符的范围大于等于父类。

构造器能否被重写

在继承方面,父类的构造器和私有属性并不能被继承,因此构造器不能被重写。(不能存在继承关系自然不能被重写)

静态变量和实例变量的区别

首先语法上静态变量要用static修饰
在程序运行时:实例变量需要创建了(new)实例对象才可以使用,静态变量不属于某个对象,而是属于类,也叫类变量,只要程序加载了类的字节码,不用创建任何实例对象就可以被分配空间。
总之实例变量需要创建对象才可被使用,而静态变量可以通过类名直接调用。

一个静态方法中不能调用类中的非静态的方法和变量

static修饰的变量在类加载后在内存中只有一份内存空间,可以被一个类的所有实例对象所共享
Java类的初始化顺序
class Test{
 public static void main(String[] args){
  new B();
 }
}

class A{
}

class B extends A{
}
类的大致加载顺序:
	静态块,普通方法块,构造方法,成员变量
实际顺序:
	父类的静态代码块 || 父类的静态成员 --->子类的静态代码块 || 子类的静态成员 --->
	父类普通代码块 || 父类的普通成员 ---> 父类的构造方法 --->子类的普通代码块 || 子类的普通成员---> 子类的构造方法
String 为什么时final的,String,StringBuilder,StringBuffer的区别
  • 被final修饰的对象不能被改变,保证了被修饰属性的安全性。
  • 为了字符串常量池更好的存储字符串。
上文提及被final修饰的对象不能被改变,是引用(对象的地址)不能变。

StringBuffer是线程安全的,多线程环境使用
StringBuilder非线程安全, 单线程使用,不同步,效率高
String放在常量池,本身就是线程安全的,但String不可变。适合操作少量数据

描述值传递和引用传递
Java中只有值传递,
class Test{
 public static void main(String[] args){
  int age=20;
  changeAge(age);
  System.out.print(age);//结果为20,是因为此时传值到方法中,产生了一个副本
  			//方法中修改的值是副本的值。

  User u=new User();
  u.setAge(20);
  changeAge(u);
  System.out.print(u.getAge());//结果为30,但传递的也不是引用,
  				//而是对象的物理内存地址
 }  
 
 private static void changeAge(int age){
  age=age+10;
 }
 
 private static void changeAge(User u){
  int temp=u.getAge()+10;
  u.setAge(temp);
 }
}

class User{
 private int age;
 get/set...
}
局部变量使用前要显式的赋值,否则编译不能通过,为啥这样设计

成员变量的赋值是由JVM操作的,可以在方法执行前后赋值,这个是在运行时发生的。
局部变量的赋值是交给编译器做的为了给编码人员一个约束,避免犯错。

自动装箱和拆箱(语法糖),==和equals
装箱就是  自动将基本数据类型转换为包装器类型;拆箱就是  自动将包装器类型转换为基本数据类型。

在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

Integer的缓存在-128-127,不在范围内则重新new一个Integer。

==:比较的是两个对象的内存地址是否相同,基本数据类型比较的是内容。
equals:两种情况:

  1. Object类中原生的equals)(),比较的是两个对象的地址
  2. 其他类对equals()进行了重写,就看具体情况
    比较包装类型用equals

异常处理方面

final,finally,finalize的区别

final修饰类,方法,属性,都是最终的,不可被继承,重写,修改。(String和Math)
finally:用于异常处理,正常情况下都会被执行,除非在此之前遇到了错误或两次return;
finalize:是Object类中的方法,调用此方法会通知GC进行垃圾回收,只是通知,不是立即执行,因此当调用此方法后对象已经被GC处理了,就会报一些错误,因此不推荐使用。

Error和Exception的区别:

首先,Error类和Exception类都是继承Throwable类首先,Error类和Exception类都是继承Throwable类

  • Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
  • Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
    • CheckedException:(编译时异常) 需要用try——catch显示的捕获,对于可恢复的异常使用CheckedException。(编码过程中编译器会报错)
      我们在编写程序过程中try——catch捕获到的一场都是CheckedException。
        io包中的IOExecption及其子类,都是CheckedException。
    • UnCheckedException(RuntimeException):(运行时异常)不需要捕获,对于程序错误(不可恢复)的异常使用RuntimeException。(编码时不会报错但运行时报错)
      illegalArgumentException:此异常表明向方法传递了一个不合法或不正确的参数。
        illegalStateException:在不合理或不正确时间内唤醒一方法时出现的异常信息。换句话说,即 Java 环境或 Java 应用不满足请求操作。
        NullpointerException:空指针异常(我目前遇见的最多的)
        IndexOutOfBoundsException:索引超出边界异常
try{}中有return ,finally会不会执行,是在return前还是return后

会执行,会在return 执行中执行。

try{
 int a=1/0;
 return 0;
}catch (Exception e){ 
 return 1;
}finally{
 return 2;
}
最终结果为:1;
因为:try中有异常,到了catch中,return 1执行,但并不会直接返回值,进入finally,执行完后
‘返回catch中的返回值。
简述Java中异常处理机制的原理和应用

Java异常处理机制保证了程序的健壮性,用它来避免我们错误操作的出现。
应用:做支付时突然断网,这是就要给用户做出提示,这就是应用。

有无自定义异常,为什么自定义异常

在调用支付宝接口时会发现它有很多错误码。后来发现这就是他们定义的自定义异常,为保证程序的健壮性,我们可以去找它的错误码去改BUG。

集合框架部分:

List,Set,Map三者的区别
  • 类结构:
    List,Set都实现了Collection接口
    Map是个顶层接口。
  • 元素重复:
    List可以重复,可以添加空值,有序,可根据下标存取
    Set不可以重复,可以添加一个空值,无序
    Map:key-value:key只能有一个空值,value可以有多个空值。默认无序,根据key的hash码查找
ArrayList,LinkedList,Vector的存储性能和特性

ArrayList是在List下的AbstractList下,与Vector平级,LinkedList是在他俩的下一级。
Vector是线程安全的实现(同步)数组增强版,可通过下标访问;利于查询,不利于插入
ArrayList非线程安全(非同步),数组增强版,可通过下标访问;效率较高;利于查询,不利于插入,实现的RandomAccess,执行随机访问
LinkedList:非同步,双向链表,通过节点访问node,node与node之间存在指向关系。利于输入,不利于查询。

查询量大用ArrayList和Vector,多线程用Vector
插入量大用LinkedList

HashMap和HashTable区别

都是实现Map接口
HashMap:继承自AbstractMap,(数组+链表+红黑树(JDK8))初始化容量为16(合数),非同步,效率高,但多线程下造成死循环
HashTable:继承自Dictionary,初始化容量为11(素数),同步,每次枷锁访问阻塞,效率变低。

ConcurrentHashMap:(JDK1.5提出的)

解决多线程下的安全和效率问题,
JDK1.8之前采用的是分段锁技术,之后采用的是CAS保证一致性。

HashSet如何保证不重复

HashSet底层就是HashMap,实际上是利用了HashMap的key不能重复,值就是个Object。
(ConcurrentHashMap中的get,clear, 迭代都是弱一致性,
强一致性是什么?就是我们在添加进去之后,再get,肯定能get到)

Iterator

Iterator使用场景:遍历集合框架(LinkedList,ArrayList,Set)
ArrayList:根据下标添加,默认加到最后,
LinkedList:与ArrayList相似,默认加到最后。
Set:底层是put
知道数据结构为List的情况下,使用一个可以添加的迭代器:ListIterator

/**
为什么有remove()而没有add()? 因为有的集合是无序的,不知到加到那去
ArrayList
**/
LinkedList<String> linked=new LinkedList<String>();
Interator<String> iterator=linked.iterator();
while(iterator.hasNext()){
  String next=iterator.next();
  iterator.remove();
  //在遍历时移除元素用迭代器
}
Iterator和Iterable的区别:
Collection所有子类都具有返回Iterator的能力
Interator负责数据结构的迭代,获取数据(1.2引入)
Iterable表示数据结构拥有迭代的能力(1.5引入)

 为什么要引入able?
 Interable中包含一个Iterator,iterator重实现,Iterable更多的是给一个规范。
Enumeration和Iterator

枚举:一个个举例拿出来,意思就是迭代。JDK1.0即引入;不能remove
Vector和HashTable都实现了Enumeration枚举功能。

Iterator<String> iterator=vector.iterator();
while(iterator.hasNext()){
  String next=iterator.next();
}

Enumeration<String> elements=vector.elements();
while(elements.hasMoreElements()){
  String nextElement=elements.nextElement();
}

当遍历集合想remove元素时,用集合的remove方法会抛异常,应该用iterator的remove。因为元素移除之后,下标就不准了。
会抛出ConcurrentModificationException,foreach是Iterator的简单实现。
在迭代器迭代时,不能对该数据进行更改,如果发生更改,就造成我们迭代出的数据不一致。
fail-fast:快速失败,是一种检测机制。
modCount():表示集合发生了多少次更改,更新次数。
expectedModCount():是迭代器自己声明的标记。
java.util下的集合框架中使用Iterator迭代器,都是采用的fail-fast,更改的为数据本身
java.util.concurrent包下的集合都是 fail-safe,安全失败,更改的数据为副本,占用空间比较大
在多线程情况下,如果一个线程在迭代该数据,如果使用Iterator,会造成modCount和expectedModCount不一致。
更多的情况下不建议直接修改,如果有这样的程序代码,建议改成fail-safe.

Comparable和Comparator的区别
  • Comparable:内部比较器
    compareTo(o1):返回int自然数(负,正,0)将传入的对象与当前对象对比
  • compare(o1,o2):外部比较器
    compare(o1,o2):一般用于不想修改类的情况

TreeMap在没有指定比较器的情况下,会按照TreeMap的内部排序方式排序。

队列和栈

  • 先进后出FILO(压栈),底层采用Vector
Stack<String> stack=new Stack<String>();
stack.push("");//添加
stack.pop();//弹出并移除
stack.peek();//弹出不移除
  • 队列
    Queue企业中经常用消息队列(流量100W,但服务器只能处理1W,那么剩下的就存在队列消息服务器中。activeMQ)
    先进先出; 还有一个双端队列–Deque<E>,本身来自于Queue,能够代替Stack。
IO
  • 按照流向来分:
    输入流:InputStream和Reader
    输出流:OutputStream和Writer

  • 按照处理单元来分:

    • 字节流:Input/OutputStream,按byte处理
      • FileInputStream
      • ByteArrayInputStream
      • BufferedInputStream
      • ObjectInputStream
      • DataInputStream

    所有文件都能处理

    • 字符流:Reader/Writer,按char处理
      • FileReader
      • CharArrayInputStream
      • BufferedReader

    只能处理文本文件

字符流和字节流相互转换:InputStreamReader/OutPutStreamWriter

AIO BIO NIO
  • BIO:block-IO 传统的阻塞IO JDK1.4之前默认使用
    • ServerSocket(在Socket进行通信时,服务器需要指派线程为当前的Socket服务,并且时阻塞式服务)
  • NIO:None-Block-IO:同步非阻塞IO,JDK1.4之后,现在用的较多的服务IO模型(netty,zooleeper)
    一个线程对应多个服务(客户端)弊端是:多个客户端请求时,单个线程不够用。
    有三个模型:(多路复用机制)
    1. 单线程模型
    2. 多线程模型
    3. 主从线程模型
  • AIO:NIO2–异步非阻塞IO,JDK1.7后支持,应用的不太多
什么叫序列化

要将一个Java对象进行网络传输(RPC)或保存到硬盘,做持久化操作。
session对象支持持久化操作。
实现Serializable接口后会有一个序列化ID,它是反序列化需要的对象

递归读取文件夹下的文件,代码实现
//给定一个路径去构造一个File
public static void getFile(String path){
  File file=new File(path);
  boolean flag=file.isDirectory();
  if(flag){
    File[] listFiles=file.listFiles();
    for(File temp:listFiles){
      if(temp.isDirectory){
      System.out.println("目录名"+temp.getName());
        getFile(temp.getPath());
      }else{
        System.out.println("是个文件"+temp.getName());
      }
    }
  }else{
    System.out.println("这就是个文件"+file.getName());
  }
}

网络编程

HTTP响应码301和302的区别

都是重定向,301:永久性重定向,302:非永久性重定向
SEO知识点:对于搜索引擎优化,301是无伤害的(对爬虫无伤害),302:有伤害的,不知道具体跳哪一个。

简述TCP和UDP
  • TCP
    • 面向连接的协议,Socket通信时需要三次握手
    • 正因为需要连接,因此会出现DDOS攻击
    • 基于流的协议
    • ServerSocket
    • 用于比较稳定,对数据有效性有很高要求的地方
    • TCP报文头较复杂
  • UDP
    • 无连接协议
    • 基于数据报(报文),不太可靠,会出现数据丢失
    • DataGramSocket,DataGramPacket
    • 应用于直播,视频,效率较高
    • 报文头简单
简述三次握手,四次挥手

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

TCP粘包

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

  • 5
    点赞
  • 3
    评论
  • 29
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值