面向过程:面向过程的性能高于面向对象,因为面向对象类的实例化和调用都需要很大的开销,如果考虑开销方面,面向过程适用与单片机,嵌入式开发等环境。
面向对象:面向对象具有易于维护,易于扩展,易于复用的特点,具有封装,继承,多态的特性,能够设计出低耦合且灵活的系统
2.Java语言的特点?1)面向对象,具有封装,继承,多态的特性
2)跨平台
3)支持多线程
4)支持网络编程
5)编译与解释共存
3.JVM,JDK,JRE之间的关系JVM:Java虚拟机,通常编译后的.class文件会在虚拟机中解释,转化成二进制机器码
JDK:包含Java编译器(javac),Java编译工具((如 javadoc 和 jdb)
JRE:包含Java核心类库,Java命令,JVM
总结:JDK包含JRE和JVM,JRE包含JVM
4.Java和C++的区别1)两者都是面向对象的语言,具有封装,继承,多态的特性
2)Java不支持指针访问内存,程序内存更加安全
3)Java只能单继承,C++可以多继承,但是Java可以通过接口实现多继承
4)Java能自动管理内存,不需要程序员手动释放内存
5)在 C 语⾔中,字符串或字符数组最后都会有⼀个额外的字符‘ ’来表示结束。但是,Java 语 ⾔中没有结束符这⼀概念。
5.字符型常量和字符串常量的区别1)字符型常量是以单引号的形式,每个字符表示ASCLL码,可以进行运算;
2)字符串常量以双引号的形式,每个字符串表示内存地址
3)字符型常量占2个字节,字符串常量占若干个字节
Java数据类型:
6.重载和重写的区别?重载:发生在同一类,多种方法根据不同的传参方式执行不同的逻辑。
使用条件:方法名相同,返回值,参数类型,参数个数,参数顺序可以不同
重写:发生在继承关系上,往往是子类对父类方法的扩展。
使用条件:“两同两小一大”。两同指的是:方法名和参数列表相同。两小指的是:子类的返回值类型必须小于等于父类;子类抛出的异常小于等于父类。一大指的是:子类的访问权限必须大于等于父类。
关于“子类返回值类型必须小于父类的理解”
如果父类的方法返回值类型是void或者基本数据类型,则子类重写时不可修改,如果是引用数据类型则子类要小于等于父类。
public class Hero {
public String name() {
return "超级英雄";
}
}
public class SuperMan extends Hero{
@Override
public String name() {
return "超⼈";
}
public Hero hero() {
return new Hero();
}
}
public class SuperSuperMan extends SuperMan {
public String name() {
return "超级超级英雄";
}
@Override
public SuperMan hero() {
return new SuperMan();
}
}
7.构造器能否被重写?
构造器不能被重写,但是可以被重载,我们经常看到的有参和无参构造器就是典型的例子。
8.封装,继承,多态封装:把一个对象的属性私有化,只提供一个接口来访问对象的属性
继承:在原有类的基础上功能的扩展,子类继承父类之后,可对方法重写增加新的功能。
子类拥有父类的所有属性和方法,但是不能被访问。
子类是否一定要重写父类的方法?
分两种情况:1.如果方法是抽象的,则必须重写 2.如果方法是普通方法,则可以不重写。
多态:一个引用变量到底指向哪一个类的实例对象,引用变量到底调用哪个类的方法在编程时不确定的,只有在运行时才能确定。
实现多态的方式有两种:1.继承(多个子类对同一个方法重写) 2.接口(实现接口并覆盖同一方法)
9.String StringBuilder StringBuffer区别1)可变性
String类是不可变长字符串,因为String类源码中有private final char[] value修饰,而StringBuilder和StringBuffer继承于AbstractStringBuilder类,也是用char[] value实现,但是没有final修饰。
2)线程安全
String类是不可变的,可以理解为常量,线程安全。StringBuilder和StringBuffer有公共的方法,但是StringBuffer对方法加了同步锁,因此线程安全。
3)性能
String是不可变长,所以只能通过new新的String对象,而StringBuilder和StringBuffer是对对象本身操作,性能更好,而且相同情况下StringBuilder比StringBuffer性能高10%-15%
对于三者使⽤的总结: 1. 操作少量的数据: 适⽤ String 2. 单线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuilder 3. 多线程操作字符串缓冲区下操作⼤量数据: 适⽤ StringBuffer
10.一个静态方法内调用非静态成员是非法的吗?是非法的,这个问题如果按照很多面试答案是:因为静态方法是属于类的,可以通过类直接调用,而非静态成员属于实例对象的,需要通过对象调用,听到这里和多人很懵逼。其实这个要理解还是要从JVM的类加载机制说起。
我们只要关注连接和初始化阶段,在连接中的准备阶段,静态变量会进行赋值,也就是初始化,而非静态变量不会进行赋值;在初始化阶段静态方法会初始化,而非静态方法不会初始化,那么非静态成员(方法和变量)什么时候初始化呢?答案是在实例化对象之后,也就是new对象之后。现在就可以很清楚的回答这个问题了,在类加载过程静态方法先初始化,这时候非静态成员还在睡大觉,就是还没初始化,那么静态方法调用非静态成员当然会出错!
11.Java中为什么要使用无参构造?因为子类在执行构造方法之前需要通过用super()调用父类中的特定构造方法,如果子类没有super(),那么会去调用父类的无参构造,这时候没有无参构造就会出差错。所以父类需要添加无参构造函数。
12.接口和抽象类的区别?接口:1.8以前方法必须是public,接口里面只能是常量变量或抽象方法;1.8时方法可以是public或者default,可以有默认方法和静态方法;1.9时可以是private,可以有私有静态方法和私有方法
2.接口中只能有static和final修饰变量
3.一个类可以实现多个接口,但是只能实现一个抽象类
抽象类:1.8以前默认是prodected;1.9默认是default
13.成员变量和局部变量的区别?1. 从语法形式上看:成员变量是属于类的,⽽局部变量是在⽅法中定义的变量或是⽅法的参数; 成员变量可以被 public , private , static 等修饰符所修饰,⽽局部变量不能被访问控制修饰 符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
2. 从变量在内存中的存储⽅式来看:如果成员变量是使⽤ static 修饰的,那么这个成员变量是属 于类的,如果没有使⽤ static 修饰,这个成员变量是属于实例的。对象存于堆内存,如果局 部变量类型为基本数据类型,那么存储在栈内存,如果为引⽤数据类型,那存放的是指向堆 内存对象的引⽤或者是指向常量池中的地址。
3. 从变量在内存中的⽣存时间上看:成员变量是对象的⼀部分,它随着对象的创建⽽存在,⽽局部变量随着⽅法的调⽤⽽⾃动消失。
4. 成员变量如果没有被赋初值:则会⾃动以类型的默认值⽽赋值(⼀种情况例外:被 final 修饰 的成员变量也必须显式地赋值),⽽局部变量则不会⾃动赋值。
14. 创建⼀个对象用什么运算符?对象实体与对象引用有何不同?new 运算符,new 创建对象实例(对象实例在堆内存中),对象引⽤指向对象实例(对象引⽤存 放在栈内存中)。⼀个对象引⽤可以指向 0 个或 1 个对象(⼀根绳⼦可以不系⽓球,也可以系⼀ 个⽓球);⼀个对象可以有 n 个引⽤指向它(可以⽤ n 条绳⼦系住⼀个⽓球)。
15.静态方法和实例方法的区别?1)静态方法的调用可以采用“类名.方法名”的方式调用还可以采用“对象名.方法名”的方式调用;但是实例方法只能通过后者
2)静态⽅法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态⽅法),⽽不允许访问实例成员变量和实例⽅法;实例⽅法则⽆此限制。
16.==和equals的区别?1)==:比较的是对象的地址。当为基本数据类型时,比较的是对象的值;当为引用数据类型时,比较的时对象的地址
2)equals:分两种情况:没重写时,它和==的作用一样,比较的是对象的地址。重写时,它比较的是对象的内容
17.hashcode()和equals()?首先了解一下什么是hashcode(),hashcode()会返回一个int类型的值,是用来获取散列码或者叫哈希码的方法,通过哈希码可以定位到哈希表的索引值。hashcode()定义在Objeck中,这就意味着Java中所有的类都有hashcode(),但是Object中的hashcode()是使用native修饰的,使用c和c++写的,返回的是一个地址,而其他类里面的hashcode()直接返回int类型的整数。
为什么要重写hashcode()?以HashSet为例,当HashSet添加值的时候,会通过hashcode()计算出hashcode值,然后找到对应的索引值,如果这个索引值上没有值则直接把添加的值更新,如果有值,也即是hashcode相同,那么就会调用equals()来进一步判断,如果返回true则两个值完全相同那么就不会加入,如果返回false,那么就会重新散列到其他位置。这样我们就⼤⼤减少了 equals 的次数,相应就⼤⼤提⾼了执⾏速度
重写equals()一定要重写hashcode()吗?为什么?一定要,首先我们要了解下,默认情况下,也就是不重写hashcode()时,每个元素的散列码是独一无二的,而如果两个对象相同,那么hashcode也一定相同,这就相互矛盾,所以重写equals()一定要重写hashcode()。
18.线程 进程 程序的区别和联系?程序包括指令和数据,指令由CPU调度,数据存储在内存中。程序简单直观的说我们平时用的QQ,网易音乐等都是一个程序,它们其实是储存在磁盘当中,如果你想登入QQ你要怎么做?首先肯定是要把一条一条指令给CPU,然后CPU通过指令调度把数据存储在内存当中,这个时候就启动了一个可执行程序------QQ。总结一下:程序包括指令和数据,指令由CPU调度,数据存放在内存中。在程序没有启动时它是静态的,启动了之后就是可执行程序。
进程:本质就是资源分配的最小单位。根据上面说的程序,其实可以理解为进程就是一个可执行程序,在控制台打开就是.exe的进程。
线程:线程其实就是在进程里边的小进程。
上面形象的说明了进程,线程,程序之间的关系,官方一点来说总结如下:
程序:程序包括指令和数据,指令由CPU调度,数据存储在内存中。当指令运行时需要用到磁盘等,当一个程序运行时,磁盘就会将这个程序的代码加载到内存,这时就启动了一个进程。
进程和线程的区别:
根本区别:进程是资源分配的最小单位,线程是CPU调度的最小单位
包含关系:一个进程包括了至少一个线程,进程之间拥有共享资源,如内存,线程共享进程的内存资源。
资源开销:通过包含关系可以知道,进程开销大,线程开更轻量,上下文切换开销小
影响关系:一个进程的崩溃不会导致另一个进程崩溃,而一个线程崩溃会造成进程崩溃,进程可独立运行,线程需要依靠进程运行。
19.线程的五种状态(六种)操作系统层面
1)新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread;
2)就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
3)运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就.绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
⒉同步阻塞―线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞―通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时. join()等待线程终止或者超时.或者I/O处理完毕时,线程重新转入就绪状态。
5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Java层面
1)NEW
2)RUNNABLE(运行状态,可运行状态,阻塞状态)
3)BLOCKED
4)WAITING
5)TIMED_WATING
6)TERMINATED
线程创建之后它将处于 NEW(新建) 状态,调⽤ start() ⽅法后开始运⾏,线程这时候处于 READY(可运⾏) 状态。可运⾏状态的线程获得了 cpu 时间⽚(timeslice)后就处于 RUNNING(运⾏) 状态。当线程执⾏ wait() ⽅法之后,线程进⼊ WAITING(等待)状态。进⼊等待状态的线程需要依靠 其他线程的通知才能够返回到运⾏状态,⽽ TIME_WAITING(超时等待) 状态相当于在等待状态 的基础上增加了超时限制,⽐如通过 sleep(long millis)⽅法或 wait(long millis)⽅法可以将 Java 线程置于 TIMED_WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状 态。当线程调⽤同步⽅法时,在没有获取到锁的情况下,线程将会进⼊到 BLOCKED(阻塞) 状态。线程在执⾏ Runnable 的 run() ⽅法之后将会进⼊到 TERMINATED(终⽌) 状态。
我们可以看到两种情况下:从操作系统来看,调用wait(),sleep(),join(),IO阻塞会进入阻塞状态;从Java层面看会进入WAITING或者TIMED_WAITING状态
20.Java中的异常从图中可以看出,Exception(异常)和Error(错误)都继承于父类Throwable类。异常它是可以通过程序员处理的,而错误一般发生在JVM的错误,这是不可避免的,当然也不能处理。
Error:包括OutOfMemoryError,StackOverFloeError等
Exception:包括IOException(受检查异常)和RuntimeException(不受检查异常),受检查异常需要catch/throw处理,不然不能通过编译,常见的受检查异常包括:IO相关,SQLException,ClassNotFindException。不受检查异常不需要处理也可以通过编译,常见的不受检查异常包括:ArithmaticException,NullPointerException,IndexOutOfBoundException等
异常处理总结:
try:用于捕获异常,可以有0或者多个catch,如果没有catch时,要有一个finally
catch:用于处理try中的异常
finally:无论是否捕获或者处理异常都会执行
在下面三种情况finally不会被执行:
1.在try或者finally中使用了System.exit(int),程序退出
2.程序的线程死亡
3.关闭CPU
21.序列化和反序列化Java 序列化:是指把 Java 对象转换为字节序列的过程;
Java 反序列化:是指把字节序列恢复为 Java 对象的过程;
举例:
Web 服务器中的 Session 会话对象,当有10万用户并发访问,就有可能出现10万个 Session 对象,显然这种情况内存可能是吃不消的。
于是 Web 容器就会把一些 Session 先序列化,让他们离开内存空间,序列化到硬盘中,当需要调用时,再把保存在硬盘中的对象还原到内存中。
一些解释:
1、Serializable 接口的作用只是用来标识我们这个类是需要进行序列化,并且 Serializable 接口中并没有提供任何方法。
2、SerialVersionUid 序列化版本号的作用是用来区分我们所编写的类的版本,用于判断反序列化时类的版本是否一直,如果不一致会出现版本不一致异常。
3、transient 关键字,主要用来忽略我们不希望进行序列化的变量
具体可看:Java序列化与反序列化 - niceyoo - 博客园 (cnblogs.com)



