java基础面试点
- java基础
- 面向对象和面向过程区别
- java语言的特点
- JVM,JRE,JDK
- Oracle JDK 和 Open JDK区别
- Java和C++的区别
- java程序的主类和java小程序主类差别
- 字符和字符串的差别
- 各个数据类型的内存大小(1字节=8bit)
- 构造器是否可以被Override(重写)?
- 重载和重写的区别:
- java面向对象三大特性(封装,多态,继承)
- String,StringBuilder,StringBuffer
- 自动装箱和拆箱
- 为什么静态方法里不可以调用非静态变量
- 不带参数的构造器的作用和必要性
- javax 和 import java区别
- 接口和抽象类的区别
- 成员变量和局部变量区别
- 对象引用和对象实例
- 方法返回值的作用
- 类的构造方法作用
- 构造方法特性
- 静态方法和非静态方法
- 判断对象内容相等和对象地址相等
- equals和==的差别
- hashCode的重写
- hashCode和equals的一些规定
- java中只有值传递导致什么,为什么
- 线程,程序,进程
- 线程的状态:
- final关键字的总结(属性,方法,类)
- java的异常和错误
- 序列化和反序列化
- 获得键盘输入的两种方法
- java中的IO流
- BIO,NIO,AIO
- static,final,this,super
- Collections和Arrays工具类
- 深拷贝和浅拷贝
- 链表(单向,双向,双向循环)
java基础
面向对象和面向过程区别
面向过程:
面向过程的运行速度相比面向对象要快(因为面向对象需要实例 化)常用于单片机,嵌入式开发,Linux等对性能要求高的开发
面向对象:
因为有封装,基础,多态等特性,所以相比于面向过程更加容易维护,扩展和复用。但是性能相对面向过程较慢
性能差主要原因:
java语言是半编译语言,最终的执行代码不是可以被CPU执行的二进制代码,而是虚拟机可以运行的字节码
java语言的特点
1.简单易学
2.面向对象(封装,继承,多态)
3.平台无关性(java虚拟机执行,不同平台运行无差别)
4.安全,可靠
5.支持多线程
6.支持网路编程
7.编译和解释并存
JVM,JRE,JDK
java代码运行的步骤:
1…java代码,通过jdk的javac编译为class文件。
2.jvm运行含有字节码的class文件,经过jvm虚拟机转换为及其可以执行的机器码
JVM:
java虚拟机,目的是使一样的字节码有着一样的运行结果
字节码解释(拓展名为.class的文件):字节码不面像任何处理器,只面向虚拟机。解决了java代码执行效率低的问题。只要有虚拟机,就可以无需重新编译,就可以在多种不同操作系统计算机上面运行。
JRE:
java运行环境(运行已编译的java程序的所有内容集合,包含JVM,java类库,java命令和其他一些基础构建)。他不能用于创建新的程序
JDK:
拥有JRE拥有的一切,还有编译器(javac)和工具(javadoc和jdb)。可以创建和编译程序。
注意:只是运行简单的java代码,装JRE就可以了。但是要创建新的程序和编程工作就要装JDK。但是如果使用JSP部署到Web时,即使不编译也要装JDK,因为服务器会将JSP转换为java servlet,这时需要用到JDK来进行编译工作。
Oracle JDK 和 Open JDK区别
1.前者不完全开源,后者完全开源。
2.Oracle和Open代码十分接近
3.前者的代码稳定和效率都要高于后者
4.。。。。
Java和C++的区别
1.都是面向对象语言,都支持封装,继承和多态
2.Java不提供指针直接访问内存,更加安全
3.Java类是单继承,C++是多继承。但是java的接口也可以多继承
4.Java有自动内存管理机制,不需要手动释放资源
5.C++字符串结束时需要加‘\0’来表示,java不需要
注意:java里面的字符串时对象,所以存储的时候不需要用‘\0’表示结束,而C++的字符串和字符数组都要比真实表示长度多1,用来表示‘\0’
java程序的主类和java小程序主类差别
java程序和小程序区别:
java程序要通过主类的main方法启动,而小程序一般是嵌入在浏览器上运行的(调用init()或者run()来启动)
java程序
可以有多个类,但是只可以有一个主类,主类是指含有main方法的那个启动类。主类不一定要求为public
java小程序
的主类时继承来自系统类JApplet或者Applet的子类。主类必须时public类
字符和字符串的差别
1.字符用‘’来表示,字符串用“”来表示,字符占2字节
2.字符常量相当于一个ASCll值,可以参与表达式运算;字符代表一个地址
下面展示一些 内联代码片
。
// a字符的ASCLL值为97,所以运行结果为99
// 测试字符的ASCLL运算
public static void main(String[] args) {
char c = 'a';
System.out.println(c+2);
}
//字符转字符串的方法
char c = 'a';
System.out.println("sdf"+c);//运行结果为sdfa
//字符串转字符(字符串长度为1)
String s = "a";
System.out.println(s);//a
char c = s.toCharArray()[0];//先将字符串转换为字符数组,再取第一个
System.out.println(c+0);//97
各个数据类型的内存大小(1字节=8bit)
构造器是否可以被Override(重写)?
重写:
是子类重写父类的方法。
构造器当然不能被重写了。但是可以被重载(Overload),参数可以不同。
重载和重写的区别:
重载(Overload):
发生生同一个类中。重载的方法方法名必须相同,但是参数类型,个数和顺序必须,返回值必须有一个不同。
重写(Override):改变了行为,不改变外貌
子类才可以重写父类的方法。方法名必须相同,重写的方法修饰符范围必须大于等于父类,返回值必须小于等于父类,抛出的异常也要小于等于父类对应的方法。
java面向对象三大特性(封装,多态,继承)
封装
//封装时属性私有化,对外提供属性的get和set方法。其他方法可以私有化可以公共。私有化后外界不能访问。只有本类可以访问
class Studnet{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
继承
子类继承父类:继承可以使用父类的功能,可以拥有父类的属性和方法。非常方便的复用前面的代码
多态
多态发生再父类或者父接口上
//这里调用的时Sun类的sayHello方法,但是父类必须有sayHello这个方法
//其实就是方法的覆盖
Father sun = new Sun();
sun.sayHello();
String,StringBuilder,StringBuffer
1.可变性
String类用private和final修饰了,是不可变化的。而StringBuilder和StringBuffer是可变的。
2.继承自
StringBuilder和StringBuffer都是继承AbstractStringBuilder这个类的。AbstractStringBuilder里面定义了char[],但是没有定义为final所以是可变的
3.线程安全性
StringBuilder是线程不安全的,StringBuffer是线程安全的。String因为是长度不可变的,所以是线程安全的。
4.性能
StrngBuilder相对StringBuffer性能高一点点,但是要冒线程不安全的险
5.应用场景
String应用于少量数据处理
StringBuilder用于单线程下操作大量数据
String Buffer用于多线程下操作大量数据
自动装箱和拆箱
基本数据类型和应用类型之间的切换
//自动装箱
Integer i = 55;
Float f = 55f;
//自动拆箱
int i2 = new Integer(55);
float f2 = new Float(55f);
为什么静态方法里不可以调用非静态变量
静态方法和静态块等构造器等实例化前就已经加载了,而非静态变量和方法必须子啊这之后才可以加载
不带参数的构造器的作用和必要性
如果类没有构造方法,则会自动带上无参构造。当有其他带参构造时就不会自动加上无参构造。
但是无参构造确实必须要写的,因为子类的构造方法里面会自动加上一行代码(super())这行代码就是掉用父类的无参构造。当父类没有无参构造时,编译就会报错。
子类构造自动加上super主要是要帮助子类做初始化工作
javax 和 import java区别
因为javaAPI包里面拓展时可能会破坏里面的一些组成部分,所以有了javax。也是API的组成部分。
其实这两个之间没什么区别。
接口和抽象类的区别
1.默认方法都是public,接口再java8之前都不能有方法实现。抽象类可以有非抽象方法的实现。
2.接口里面的属性只可以时static,final类型的。而抽象类没有限制
3.抽象类只可以单继承,接口可以多继承
4.抽象类的抽象方法不能时private,分抽象方法可以
成员变量和局部变量区别
class Studnet{
//1.
//成员变量可以被private等修饰符修饰,但是局部变量不可
//成员变量和局部变量都可以被final修饰
private final String name = "sdf";//属于实例
public static int age;//属于Student类,这里会自动赋值为0
public void jubu(int time2){//局部变量
final int time;//局部变量
}
//2.
//局部变量属于方法,再方法内部或者为方法参数
//成员变量在没加static时属于实例,在加static后属于类。
//局部变量因为时方法的,所以储存在栈帧里面
//因为类储存在堆里面,所以成员变量在堆
//3.
//成员变量随着类的创建而存在,随着类的回收而消失
//局部变量随着类方法的创建而存在,随着方法的结束而消失
//3.
//成员变量没有赋值的话会自动被赋初值
//局部变量不会被自动赋值
}
对象引用和对象实例
1个对象引用可以指向0个或者1个对象实例
一个对象实例可以被0个或者多个对象引用指向
方法返回值的作用
一个方法执行结束后到返回的结果。返回值可以用于其他有用的操作
在方法里的作用:可以让方法到return前面一行结束
类的构造方法作用
构造方法是用来实例化对象的。如果没有写构造方法会自动加上一个无参构造
构造方法特性
1.方法名和类名一致
2.没有返回值,void不可以写
3.new对象时根据传入对象的参数自动调用相应的构造方法
静态方法和非静态方法
静态方法
静态方法用static修饰,静态方法属于类,可以通过类.方法名直接调用。静态方法不可以调用非静态方法和属性
非静态方法
只有对象的实例才可以调用非静态方法,可以访问类的任意属性
判断对象内容相等和对象地址相等
重写equals方法,用于判断对象内容相等
class Student{
public String name;
public int age;
//重写qeuals方法用于判断对象内容是否相等
@Override
public boolean equals(Object obj) {
if(this.name.equals(((Student) obj).name) && this.age==((Student) obj).age)
return true;
return false;
}
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "sdf";
s1.age = 55;
Student s2 = new Student();
s2.name = "sdf";
s2.age = 55;
Student s3 = new Student();
s3.name = "aaa";
s3.age = 54;
System.out.println(s1.equals(s2));//true
System.out.println(s1.equals(s3));//false
}
equals和==的差别
equals:
equals方法用于判断内容是否相等。当要判断对象时,需要对象重写equals方法
==:
用于判断对象地址是否相等
public static void main(String[] args) {
//想s1和s2这样的基本数据类型都是放在常量池的
//所以可以用==判断内容是否相等
String s1 = "aa";
String s2 = "aa";
String s3 = new String("aa");
String s4 = new String("aa");
System.out.println(s1==s2);//true
System.out.println(s3==s4);//false
}
hashCode的重写
hashcode是获取哈希值,页称为散列码。其实就是一个int型数值。哈希码的作用就是确定在hash数组中的下标位置。hashcode方法定义在Object类中,意味着任何类都有这个方法。
为什么要有hash Code
通过对象的哈希值找到对应的哈希表的下表,然后再以链表,二叉树,红黑书等形式存储数据。hashCode结果算法得到下标。加快数据的查询。
hashCode和equals的一些规定
1.对象相等,hashCode和equals一定相等
2.hashCode或者equals相等,对象不一定相等
3.只要equals方法被覆盖,hash Code方法也必须要覆盖。
4.两个对象的hash值可能一样,这时候就要加上equals来判断对象内容是否一样,如果一样,两对象相等,指向同一对象。
5.hashCode如果没有重写,那么每个对象的hash值都是独特的。不可能相等。(即使两个对象指向一致,因为hash是对堆上面的对象产生的)
java中只有值传递导致什么,为什么
对方法参数的影响
方法参数为基本数据类型:
public static void main(String[] args) {
int i = 5;
Student.doIt(i);
System.out.println(i);//5
}
public static void doIt(int i){
i = 10;
}
方法参数为对象(即引用):
class Student{
public String name;
public static void main(String[] args) {
Student s = new Student();
s.name = "为改变的值";
doIt(s);
System.out.println(s.name);
}
//因为传入的值是引用类型,所以会将参数绑定
//因为java中只有值传递,没有对象传递。所以对象是被引用绑定
public static void doIt(Student s){
s.name = "改变的值";
}
}
线程,程序,进程
线程:
比进程更加小的一个单位。一个进程可以产生多个线程。同类的多线程共享工作内存和系统资源。所以说是轻量级的进程
程序:
含有指令和数据的文件,存储再磁盘或者其他数据存储设备中。也可以说是静态的代码。
进程
进程是程序的一次执行过程。是动态的。一个进程就是一个程序的创建,运行直到消亡。当进程再执行的时候,会被操作系统纳入内存中。
线程的状态:
final关键字的总结(属性,方法,类)
- final修饰字分别修饰基本数据类型和引用类型,基本数据类型只能被赋值一次。引用类型只能指向一次实例,都不能更改。
- final修饰的类不能够被继承。,而且final类的所有方法都会变成final类型的。
- final方法的使用原因:final方法能防止子类修改。final再java低版本时可以提高性能。但时现在已经不需要这样来提高性能了。
注意:private方法自动带上了final
java的异常和错误
错误
错误时程序不能处理的错误。发生时一般线程都会被终止。
异常
异常时程序本身可以处理的异常,而非错误
重点:所有的异常类都继承自Throwable类
异常机构图:
自定义异常:
1.自定义异常类可以继承Throwable类或者Exception,而不要继承Error类。自定义异常类之间也可以有继承关系
2.需要为自定义异常类设计构造方法,以方便构造自定义异常对象。
//自定义异常类
class MyException extends Exception{
public MyException(String msg){
super(msg);
}
}
//异常类的调用
try {
throw new MyException("要返回的异常信息");
} catch (MyException e) {
e.printStackTrace();
}
几个重要的异常类:都继承自Exception
- RuntimeException:运行时异常
- NullPointerException:空指针异常
- ArithmeticException:算术异常
- ArrayIndexOutofBoundsException:数组越界异常
Throwable类的常用方法
- getMessage:返回异常发生时的简要错误
- toString:返回异常发生时的详细错误
- getLocalizedMessage:返回异常对象的本地化信息
- printStackTrace:在控制台打印错误信息
注意点
- try,catch和finally块用于处理发生的异常。try块用于存放要运行的代码。当这一块的代码出错就会跳转到catch块。finally块是最后处理模块,无论是否try块出现错误都会跳转到这一块去。
- finally块关闭的集中情况:该块第一行发生了异常。finally块的前面块的代码出现了exit,表示已经退出程序。程序所在的线程死亡。关闭CPU
重点注意:
当finally块存在return,而前面块也存在return,则会返回finally的。
public static void main(String[] args) {
System.out.println(new Test2().haha());//返回的是2
}
public int haha(){
try{
return 1;
}finally {
return 2;
}
}
序列化和反序列化
反序列化
只要是属性为transient就可以防止序列化
序列化
类的序列化要继承Serializable类
public static void main(String[] args) {
sun s = new sun();
s.name = "sb";
//类的序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(
new BufferedOutputStream(bos));
oos.writeObject(s);//对象序列化后才可以写入
oos.flush();
//对象的反序列化
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(
new BufferedInputStream(bis));
sun s2 = (sun)ois.readObject();
System.out.println(s2.name);//sb
} catch (Exception e) {
e.printStackTrace();
}
}
//这个是要序列化的类
static class sun implements Serializable {
public String name;
public transient int age;//反序列化的属性
}
获得键盘输入的两种方法
Scanner s = new Scanner(System.in);
System.out.println(s.nextLine());
BufferedReader b = new BufferedReader(
new InputStreamReader(System.in));
String s = b.readLine();
java中的IO流
IO流的分类图
有了字节流为什么还要有字符流:
字节流有时候在转换为字符串后会出现字符集等错误,而且读取和写入一个字符串文件如果每次都要用字节流的话会严重的降低效率。
所以像字符串文件都可以用字符流读写。想视频等文件就必须要用字节流了。
BIO,NIO,AIO
BIO:同步阻塞式(阻塞在一个线程进行IO操作)
NIO:同步非阻塞式(不会阻塞在一个线程)
AIO:异步非阻塞式(应用不是很广泛)
static,final,this,super
static:
可以声明为静态方法,静态类或者静态属性。
final:
详细请看上面(目录里面有)
this
表示访问当前对象
super
表示访问父类对象的构造方法
Collections和Arrays工具类
public class Test2 {
public static void main(String[] args) throws IOException {
Sun s1 = new Sun();
s1.age = 10;
Sun s2 = new Sun();
s2.age = 20;
List<Sun> ss = new ArrayList<>();
ss.add(s1);
ss.add(s2);
Collections.sort(ss);
System.out.println(ss.get(0).age);
}
}
class Sun implements Comparable {
public int age;
@Override
public int compareTo(Object o) {
//这样写是从小到大排序
return this.age - ((Sun) o).age;
}
}
深拷贝和浅拷贝
浅拷贝:建立在值传递上面。
深拷贝:创建一个新的对象,再将要拷贝的对象里面的值传递给创建的对象。
深拷贝实现代码:(两种方式)
方式1:实现Cloneable接口,重写clone方法
package com.how2java.tmallweb_springboot.test;
import java.io.*;
public class Test2 {
public static void main(String[] args) throws IOException {
Demo demo = new Demo();
demo.setName("隔壁老王");
Demo2 demo2 = new Demo2();
demo2.setMsg("老裴");
demo.setDemo2(demo2);
//这样就可以实现对象的深拷贝了
Demo demo1 = demo.clone();
System.out.println(demo1.getName());
System.out.println(demo1.getDemo2().getMsg());
}
}
//这是主类
class Demo implements Cloneable{
private String name;
//里面包含要拷贝的副类
private Demo2 demo2;
@Override
protected Demo clone(){
Demo demo = null;
//这里是浅的复制(因为Demo2还没有复制过来)
try {
demo = (Demo)super.clone();
//这样才可以可以实现彻底的拷贝
demo.demo2 = demo2.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return demo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Demo2 getDemo2() {
return demo2;
}
public void setDemo2(Demo2 demo2) {
this.demo2 = demo2;
}
}
//副类
class Demo2 implements Cloneable{
private String msg;
@Override
protected Demo2 clone() throws CloneNotSupportedException {
return (Demo2)super.clone();
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
方式2:用序列化实现
1.像上面一样的Demo和Demo2两个类都要继承Serializable接口
2.用对象输出流将要拷贝的对象输出到字节数组流里面
3.得到对象的字节数组,再用对像输入流和字节数组输入流读取字节数组,再用readObjecdt()方法将读取到的对象强制转型赋值给新的对象
链表(单向,双向,双向循环)
单向:只有next
双向:
双向循环: