Java复习篇1-入门知识
一、Java入门
Java语言特点
简单易学、面向对象、平台无关性、可靠性、安全性、支持多线程(C++11开始引入了多线程库)、支持网络编程且很方便、编译与解释并存。
JVM,JDK,JRE
- JVM:运行字节码(.class文件)的虚拟机,根据不同的操作系统有不同的实现,目的是使用相同的字节码,在不同的系统上可以得到相同的结果。
- 字节码并不针对一种特定的机器,只面向虚拟机,Java语言通过字节码的方式,在一定程度上解决了传统型解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。
- Java程序从源代码到运行一般3步:源代码–>字节码–>机器码
- 字节码–>机器码:JVM类加载器首先加载字节码,然后解释器逐行解释执行,这种方式下执行速度相对较慢,之后引入JIT编译器(运行时编译),当JIT完成第一次编译后,会将字节码对应的机器码保存下来,下次可以直接使用。(编译与解释并存)
- HotSpot采用惰性评估做法,根据二八定律,消耗大部分系统资源的只有小部分代码即热点代码,JIT需要编译的就是热点代码。JVM会根据代码每次执行的情况收集信息,并相应的做出一些优化,因此执行次数越多,速度就越快。
- JDK9引入新的编译模式AOT,将字节码编译成机器码,避免了JIT预热等各方面的开销,但编译质量比不上JIT编译器
总结:JVM是运行字节码(.class文件)的虚拟机,根据不同的操作系统有不同的实现,目的是使用相同的字节码,在不同的系统上可以得到相同的结果。字节码和不同系统的JVM实现就是Java语言一次编译到处运行的关键。
- JDK:功能齐全的Java SDK,拥有JRE,还有编译器(javac)和工具(如javadoc,jdb),能够创建和编译程序。JDK=JRE+编译器+工具
JRE:Java运行时环境,是运行已编译Java程序所需的所有内容的集合,包括JVM,Java类库,Java命令和其他一些基础构件。JRE=JVM+Java类库+Java命令+基础构件
Oracle JDK,Open JDK
- Oracle JDK大概6个月发行一次,Open JDK大概每3个月发布一次;
- Open JDK是一个参考模型,完全开源,Oracle JDK是Open JDK的一个实现,不完全开源;
- Oracle JDK比Open JDK更稳定,Oracle JDK有更多的类和一些错误修复;
- 在响应性和JVM性能上,Oracle JDK比Open JDK性能更好;
- Oracle JDK不会为即将发布的版本提供长期支持,用户每次都必须通过更新到最新版本获得支持来获取最新版本;
- Oracle JDK根据二进制代码许可协议获得许可,Open JDK根据GPL v2许可协议获得许可。
Java和C++的区别
都是面向对象,都支持封装、继承、多态
区别:
- Java不提供指针来直接访问内存,程序内存更加安全
- Java的类都是单继承,C++支持多重继承,Java可以通过接口实现多继承
- Java有自动内存管理机制,C++需要手动释放无用内存
- 在C语言中字符串或字符数组有一个结束符’\0’,Java中没有结束符这一概念
Java程序的主类
主类是Java程序执行的入口。
Java应用程序和小程序之间的差别
二者主要区别在于主类不同。
- Java应用程序的主类是包含main()方法的类,Java小程序中,主类是一个继承自系统类JApplet或Applet的子类;
- 应用程序的主类不一定要求是public类,但是小程序的主类要求必须是public类。
- 应用程序时从主线程(main()方法)启动,applet小程序没有main方法,主要是嵌在浏览器页面上运行(调用init()或者run()方法来启动),这点和flash的小游戏类似。
import java和javax的区别
最开始javax只是扩展API包来使用,后来,javax逐渐扩展为Java API的组成部分,但将javax包移动到java包太麻烦,最终会破坏一些现有的代码,最终决定让javax包成为标准API的一部分。实际上,java和javax没有区别。
为什么说Java语言“编译与解释并存”
编译型语言:编译器针对特定的操作系统将源代码一次性翻译成可执行的机器码;
解释型语言:解释器对源代码逐行解释成特定平台的机器码并立即执行。
Java程序要经过先编译(生成字节码),后解释(生成机器码)两个步骤,所以说Java是编译与解释并存。
二、Java语法
字符型常量和字符串常量的区别
- 形式上:前者是单引号引起的一个字符,后者是双引号引起的若干字符。
- 含义上:前者相当于一个整型值(ASCII码),可以参与表达式运算,后者代表一个地址值(该字符串在内存中的地址)。
- 占内存大小:前者只占2个字节,后者占若干个字节
注释
单行注释//, 多行注释/* */,文档注释;
标识符和关键字的区别
标识符就是一个名字,关键字是特殊的标识符。
常见的关键字
Java序列化中有些字段不想进行序列化(transient)
使用transient关键字修饰
transient关键字作用:阻止实例中 用此关键词修饰的变量 序列化;当对象被反序列化时,transient修饰的变量值不会被持久化和恢复;transient只能修饰变量,不能修饰类和方法
volatile
native
synchronized
final
用在三个地方:类,方法、变量。
- final变量:基本数据类型的final变量初始化之后不能更改;引用类型的final变量初始化之后不能再指向另一个对象。
- final类:不能被继承,所有成员方法隐式指定为final方法
- final方法:方法锁定,防止任何子类修改。早期Java版本中会将final方法转为内嵌调用,提升性能,现在Java不需要这种方式优化。类中的private方法都隐式指定为final方法。
自增自减运算符
放在操作数之前:b=++a; 先自增/减,再赋值。
放在操作数之后:b=a++; 先赋值,再自增/减。
continue break return
continue:跳出当前这一次循环,继续下一次循环
break:跳出循环,执行循环体下面的语句
return:跳出所在方法,结束该方法的运行
Java泛型,类型擦除,常用的通配符
Java泛型:JDK5之后引入的新特性,提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型,本质上是参数化类型,即 所操作的数据类型被指定为一个参数。
类型擦除:Java的泛型是伪泛型,因为Java在编译期间,所有的泛型信息都会被擦除。如在代码中定义List和List,在编译后都变成了List,JVM看到的只是List。
泛型的三种使用方式:泛型类,泛型接口,泛型方法
通配符:
- T 具体的一个java类型
- K V 键值中的key value
- E 代表Element
== equal区别
equal在重写之前和==用法一致,重写之后,比较的是对象的内容
==作用:基本数据类型比较值,引用数据类型比较内存地址。
hashCode() equal()
- hashCode()
hashCode()作用是获取哈希码,哈希码作用是确定对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。虽然,每个Java类都包含hashCode() 函数。但是,仅仅当创建某个“类的散列表”时,该类的hashCode() 才有用(作用是:确定该类的每一个对象在散列表中的位置;其它情况下(例如,创建类的单个对象,或者创建类的对象数组等等),类的hashCode() 没有作用。上面的散列表,指的是:Java集合中本质是散列表的类,如HashMap,Hashtable,HashSet。也就是说:hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。 - 为什么要有hashCode()?
以hashSet为例,对象加入hashSet时首先计算对象的hashCode值,与已加入的对象的hashCode值作比较,如果发现有相同的值,调用equals()方法进一步检查对象是否相等,如果相等,hashSet不会让其加入成功,否则重新散列到其他位置。 - 为什么重写equals()时必须重写hashCode()?
如果两个对象相等,则hashCode值一定相等,如果没有重写,hashCode值无论如何都不相等。
三、基本数据类型
基本数据类型 包装类型
包装类型:Integer,Short,Long,Byte,Character,Float,Double,Boolean。
注意:long类型的数据一定要在数值后加L,否则作为整型解析。
自动装箱与拆箱
装箱:将基本类型用对应的引用类型包装起来
拆箱:将包装类型转换为引用类型
基本数据类型的包装类和常量池
Java基本数据类型的包装类大多都实现了常量池技术,Integer,Short,Long,Byte,Character,Boolean;前面4种默认创建了数值[-128,127]的相应类型的缓存数据,Character类型创建了[0,127]范围的缓存数据,Boolean直接返回true false。如果超出范围才会创建新新对象,Float,Double并没有实现常量池技术。
四、方法(函数)
方法的返回值
返回值:某个方法体种的代码执行后产生的结果。接收结果,使其可用于其他操作。
Java中只有值传递
按值调用:方法接收的是调用者提供的值
按引用调用:方法接收的是调用者提供的变量地址。
方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。
Java总是采用按值调用的。
一个方法不能修改一个基本数据类型的参数,不能让对象参数引用一个新对象,但是可以改变对象参数的状态。
重载和重写
- 重载:发生在同一个类中,方法名必须相同,参数类型、个数、顺序、方法返回值、访问修饰符可以不同。
- 重写:发生在运行期,方法名、参数列表必须相同,抛出的异常范围小于等于父类,访问修饰符大于等于父类。
- 若父类方法的访问修饰符有private final static修饰时,该方法不能被重写。但是被static修饰的方法可以被再次声明。构造方法不能被重写。
深拷贝和浅拷贝
- 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新对象,并复制其内容。
- 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递
方法的四种类型
- 无参无返回值
- 有参无返回值
- 无参有返回值
- 有参有返回值
五、面向对象
类和对象
面向对象 面向过程
- 面向对象:易维护、易复用、易扩展,性能比面向过程低。
- 面向过程:性能比面向过程高,没有面向对象易维护、易复用、易扩展
构造器不能被重写,可以被重载
构造方法的特性:
- 名字与类名相同
- 没有返回值,不能用void声明构造函数
- 生成类的对象时自动执行,无需调用。
无参构造方法的作用
Java程序在执行子类的构造方法之前,如果没有用super方法调用父类特定的构造方法,则会调用父类中默认的午餐构造方法。如果父类中只定义了有参构造方法,子类又没有调用,这时默认调用无参构造方法,父类中又找不到该构造方法,编译时将发生错误。
注意:如果重载了有参构造方法,记得把无参构造方法也写出来。
调用子类构造方法前先调用父类的构造方法,作用是帮助子类做初始化工作。
成员变量 局部变量
- 语法:成员变量属于方法,可以被public private static等修饰,局部变量属于方法,不能被访问控制符以及static修饰。二者都可被final修饰
- 在内存中的存储方式:成员变量属于实例(被static修饰,则属于类),对象存放于对中,局部变量存于栈中。
- 变量在内存中的生存时间:成员变量时对象的一部分,随对象的创建而存在,局部变量随方法的调用而自动消失
- 初始值:成员变量未被赋值则自动以类型的默认值而赋值,局部变量则不会自动赋值。
创建对象 对象实体和对象引用
创建对象用new运算符,对象实例存放于堆中,对象引用指向对象实例,存放于栈中。
对象相等 和 指向他们的引用相等
对象相等比较的是内存中存放的内容是否相等,引用相等比较的是指向的内存地址是否相等。
面向对象的三大特性
封装
把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法
继承
使用已有的类的定义作为基础,建立新类的技术。
注意:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和私有方法子类无法访问,只是拥有
- 子类可以对父类进行扩展
- 子类可以重写父类的方法
多态
程序中定义的引用变量所指向的具体类型和通过该变量发出的方法调用在编程时不确定,而在程序运行期间才确定。
实现多态的方法:继承和接口
形式:父类 = new 子类();
修饰符
静态方法
- 静态方法内不能调用非静态变量,也不可以访问非静态变量成员。
- 静态方法与实例方法的区别:
- 静态方法可以使用“类名.方法名"调用,也可以使用”对象名.方法名“调用,实例方法只能使用后面这个方式。
- 静态方法在访问本类成员时,只允许访问静态成员(静态成员变量和静态方法),实例方法无此限制。
接口和抽象类
区别:
- 接口方法不能有实现,(jdk8之后接口方法才可以有默认方法和静态方法功能,jdk9在接口中引入了私有方法和私有静态方法)而抽象类可以有非抽象方法
- 接口中除了static final变量,不能有其他变量,抽象类没有此限制
- 一个类可以实现多个接口,但只能继承一个抽象类,接口本身也可以用extends关键字扩展
- 接口方法默认修饰符时public,抽象方法可以有public protected default这些修饰符(不能被private修饰)
- 设计层面上看,抽象是对类的抽象,是一种模板设计,接口是对行为的抽象,是一种行为的规范
String StringBuffer StringBuilder
- 可变性:String对象不可变(final修饰),而后两者是可变的
- 线程安全:String是线程安全的(不可变),StringBuffer对方法加了同步锁或对调用的方法加了同步锁,是线程安全的。 StringBuilder是非线程安全的。
- 总结:操作少量数据用String,单线程操作字符串缓冲区下的大量数据用StringBuilder,多线程操作字符串缓冲区下的大量数据用StringBuffer。
获取键盘输入
- Scanner
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String s = sc.nextLine();
}
- BufferedReader
import java.io.*
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = "";
while((s = br.readLine()) != null){
// 代码块
}
六、异常
祖先:java.long包中的Throwable类,两个重要子类:Error(错误)和Exception(异常)
Error:程序无法处理的错误。大多数错误与代码编写者执行的操作无关,而表示代码运行时JVM出现的问题,如OutOfMemoryError,这些异常发生时,JVM一般会选择线程终止。
Exception:程序本身可以处理的异常。可以通过try-catch语句捕获并处理异常,如NullPointerException等。又分为受检查异常(必须处理)和不受检查异常(可以不处理)
受检查异常没有处理的话无法通过编译;不受检查异常不处理可以正常通过编译。
Throwable类常用方法
public String getMessage()
public String toString()
public String getLocalizedMessage()
public void printStackTrace()
try-catch-finally
try:捕获异常
catch:处理捕获的异常
finally:无论是否捕获到异常,finally里的代码块都会被执行
注意:
- try/catch块中遇到return语句,finally语句块将在方法返回前被执行。
- try和finally语句块中都有return语句时,finally语句中的return 返回值会覆盖原始返回值。
特殊情况(finally不被执行): - finally语句块第一句发生了异常
- 前面代码中用了System.exit(int)退出程序,若该语句在异常之后。finally还是会执行。
- 程序所在线程死亡
- 关闭cpu
try-with-resources
面对必须要关闭的资源,优先使用try-with-resources语句;
通常使用分号隔开,可以在try-with-resources块中声明多个资源。
七、文件与I/O流
IO流分类
按照流的流向分:输入流和输出流
按照操作单元分:字节流和字符流
按照流的角色分:节点流和处理流
4个抽象类基类:
InputStream/Reader:输入流基类,前者字节流,后者字符流
OutputSream/Writer:输出流基类,前者字节流,后者字符流
有了字节流,为什么还要字符流
文件读写、网络发送接收,信息的最小存储单元都是字节,为什么还需要字符流呢?
字符流是JVM将字节转换得到的,这个转换过程非常耗时,而且如果不知道编码类型还会出现乱码问题。所以I/O流干脆提供了一个可以直接操作字符的接口,方便对字符进行流操作。音频、图片等媒体文件用字节流比较好,涉及到字符的话用字符流比较好。
BIO,NIO,AIO
Java 语言对操作系统的各种 IO 模型的封装。
同步 异步
- 同步:发起一个调用后,被调用者未处理完请求之前,调用不返回。
- 异步:发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
阻塞 非阻塞
- 发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
- 发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
同步阻塞、同步非阻塞和异步非阻塞又代表什么意思呢?
举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在哪里傻等着水开(同步阻塞)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(同步非阻塞)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(异步非阻塞)。
BIO:同步阻塞I/O模式
数据的读取写入必须阻塞在一个线程内等待其完成。适用于低负载、低并发的应用程序。
NIO:同步非阻塞I/O模式
支持面向缓冲的,基于通道的I/O操作方法。适用于高负载、高并发的(网络)应用。
AIO:异步非阻塞I/O模式
基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
详细可参考Java面试常考的 BIO,NIO,AIO 总结