ZERO
持续更新 请关注:https://zorkelvll.cn/blogs/zorkelvll/articles/2018/12/03/1543842689567
背景
本文主要记录java相关的基础知识点,供平日里总结学习使用!
20181208
1、java异常处理
- java所有异常均继承于Throwable类,有两个重要的子类Exception(异常)和Error(错误)
- Error:程序无法处理的错误,表示运行应用程序中存在较严重的问题;一般错误与代码编写执行的操作无关即业务逻辑无关,而表示代码运行时JVM出现的问题!例如,JVM运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现OutOfMemoryError,这些错误发生时JVM一般会选择线程终止!
- Exception:程序本身可以处理的异常,子类RuntimeException、NullPointerException、ArithmeticException
- 异常可以被程序本身处理,错误是无法处理的
- Throwable类常用方法:getMessage()返回异常发生时的详细信息;toString()返回异常发生时的简要描述;getLocalizedMessage()返回异常对象的本地化信息,使用Throwable的子类覆盖该方法可以声称本地化信息,如果没有覆盖则与getMessage()返回结果相同;printStackTrace()在控制台上打印Throwable对象封装的异常信息
- 异常处理总结:try块-用于捕获异常,其后可以零个或多个catch块,如果没有catch块则必须跟一个finally;catch块-用于处理try捕获到的异常;finally块-无论是否捕获或处理异常,finally块里的语句都会被执行,,,当在try或catch中存在return时则finally语句块将在方法返回之前被执行!
- 在以下4种特殊情况下,finally块不会被执行:(1)在finally语句块中发生了异常;(2)在前面的代码中用了System.exit()退出程序;(3)程序所在的线程死亡;(4)关闭CPU
2、java序列化中如果有些字段不想进行序列化,可以怎么办?
可以使用transient关键字修饰,该关键字作用是阻止实例中那些用此关键字修饰的变量序列化,当对象被反序列化时被transient修饰的变量不会被持久化和恢复;transient只能修饰变量,不能修饰类和方法
3、获取用键盘输入的两种常用方法
-
方法1:Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close(); -
方法2:BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
4、Java基础学习书籍
- 《Head First Java (第二版)》
- 《Java核心技术卷1、卷2》
- 《Java编程思想(第四版)》
20181205
1、为什么java中只有值传递?
在程序设计语言中,按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按 值传递的。
2、程序 VS 进程 VS 线程
- 程序:含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也即程序是静态的代码
- 进程:程序的一次执行过程,是系统运行程序的基本单位,是动态的!系统运行一个程序,即是一个进程从创建,运行到消亡的过程!也即,一个进程就是一个执行中的程序,它是计算机中一个指令接着一个指令地执行着,同时每个进程还占有某些系统资源如CPU时间、内存空间、文件、输入输出设备的使用权等等!当程序在执行时,将会被操作系统载入内存中!
- 线程:线程是进程划分成的更小的运行执行单元,与进程最大的不同在于各个进程是独立的,而各个线程则不一定;进程是操作系统的范畴,主要是同一段时间内,可以同时执行一个以上的程序,而线程则是在同一个程序内几乎同时执行一个以上的程序片段!同类的多个线程共享同一块内存空间和一组系统资源,因此线程的负担笔进程小得多,也被称为轻量级进程
3、线程有哪些基本状态?这些状态是如何定义的?
- 新建new:新创建了一个线程对象
- 可运行runnable:线程对象被创建后,其他线程如main线程调用了该对象的start方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权
- 运行running:可运行状态runnable的线程获得了cpu时间timeslice,执行程序代码
- 阻塞block:阻塞状态是指线程因为某种原因放弃了cpu使用权,即让出了cpu timeslice,暂时停止运行,直到线程进入可运行runnable状态,才有机会再次获得cpu timeslice转到运行running状态!阻塞情况分三种:(1)等待阻塞:运行running的线程执行o.wait方法,JVM会把该线程放入到等待队列waiting queue中;(2)同步阻塞:运行running的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入到锁池lock pool中;(3)其他阻塞:运行running的线程执行Thread.sleep(long ms)或t.join()方法时,或者发出了I/O请求,JVM会把该线程置为阻塞状态,当sleep状态超时join等待线程终止或超时或I/O处理完毕后,线程重新转入可运行runable状态
- 死亡dead:线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期,死亡的线程不可再次复生
备注: 可以用早起坐地铁来比喻这个过程:
还没起床:sleeping
起床收拾好了,随时可以坐地铁出发:Runnable
等地铁来:Waiting
地铁来了,但要排队上地铁:I/O阻塞
上了地铁,发现暂时没座位:synchronized阻塞
地铁上找到座位:Running
到达目的地:Dead
4、关键字final
final主要用于变量、方法、类三个地方;
- 对于一个final变量,如果是基本数据类型的变量,则其数值一旦初始化之后便不能更改;如果是引用类型的变量,则在其初始化之后便不能再让其指向另一个对象
- 当final修饰一个类时,则这个类不能被继承,且final类中的所有成员方法都会被隐式地指定为final方法
- final方法的使用场景:一是把方法锁定,防止任何继承类修改它的含义;二是效率,早期java实现版本中final方法会被转为内嵌调用,但如果方法过于庞大则内嵌调用无法带来任何性能提升且现在的java版本已不再使用final方法进行这些优化了!类中所有private方法都隐式地被指定为final
20181204
1、自动装箱与拆箱
- 装箱:将基本类型用它们对应的引用类型包装起来
- 拆箱:将包装类型转换为基本数据类型
2、为什么静态方法内调用一个非静态成员是非法的?
这是因为静态方法是属于类的,不能够通过对象进行调用,所以在静态方法里不能使用其他非静态变量以及其他非静态方法
3、java中定义一个无参数且没有功能的构造方法的作用是啥?
java程序在执行子类的构造方法之前,如果没有使用super()来调用父类特定的构造方法,则会调用父类中“无参构造犯法” => 因此,若父类中只定义了有参构造方法,且在子类构造方法中又没有用super()来调用父类的特定构造方法,则编译时将发生错误! => 这是因为java程序在父类中找不到无参构造方法可以执行,,,解决办法就是在父类中加上一个无参且没有什么功能的构造方法即可!
在调用子类构造方法之前会先调用父类无参构造方法,其目的是:帮助子类做初始化工作
4、import java 和 import javax的区别
早期,JavaAPI所必需的的包是java开头的包,javax只是扩展API包;;随着时间的推移,javax逐渐也成为了JavaAPI的组成部分,实际上现在java和javax没有区别
5、接口 VS 抽象类
- 接口中方法默认是public,jdk8之前接口中的所有方法均不能有实现,必须通过实现类实现;抽象类中可以有非抽象的方法
- 接口中的实例变量默认是final类型的,抽象类则不一定
- 一个类可以实现多个接口,但最多只能实现一个抽象类
- 一个类实现接口则需要实现接口的所有方法,而抽象类则不一定
- 接口不能用new实例化,但可以声明,但是必须引用一个实现该接口的对象;从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是对行为的抽象,是一种行为的规范
6、成员变量 VS 局部变量
- 语法上,成员变量属于类,局部变量是在方法中定义的变量或方法的参数;成员变量可以被public、static等修饰符修饰,而局部变量则不能;但是,均能被final所修饰
- 存储方式上,成员变量是对象的一部分,因此是存在于堆内存中的,而局部变量则是存在于栈内存中的
- 生存周期上,成员变量是对象的一部分,随着对象的创建而存在,而局部变量则是随着方法的调用而自动消失
- 成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(例外情况是被final修饰但没有被static修饰的成员变量必须显示地赋值),而局部变量则不会自动赋值
7、对象实体 VS 对象引用 ;; 用什么运算符创建一个对象?
new运算符创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中);一个对象引用可以指向0或1个对象,一个对象可以有n个引用指向它
8、静态方法 VS 实例方法
- 调用方式:静态方法,既可以是“类名.方法名”,也可以是“对象名.方法名”,而实例方法则只有后者;即,调用静态方法不需要创建对象
- 静态方法在访问本类的成员时,只允许访问静态成员变量或静态方法,而不允许访问实例成员变量和实例方法;实例方法则无此限制
9、对象的相等 VS 引用的相等
对象的相等,比较的是在内存中存储的内容是否一致;而引用的相等,比较的则是它们所指向的内存地址是否相等
10、 == VS equals()
- ==:比较的是两个对象的地址是否相等,即判断两个对象是否是同一个对象(基本数据类型==比较的是值,引用数据类型比较的是内存地址)
- equals():判断两个对象是否相等,分两种情况:
(1)类没有重写覆盖equals()方法,则equals方法,等价于==
(2)类重写覆盖equals()方法,一般都是判断两个对象的内容是否相等的,若它们的内容相等则返回true
=>当创建String类型对象时,虚拟就就会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它直接赋给当前引用,否则就在常量池中重新创建一个String对象
11、hashCode() VS equals()
重写过hashCode()和equals()嘛?为什么重写equals时必须重写hashCode方法?
- hashCode:该方法主要是获取哈希码,也即散列码,实际上就是一个int整数值,哈希码的作用是确定该对象在哈希表中的索引位置;hashCode方法定义在Object类中,因此java所有类均包含有hashCode方法;散列表存储的是键值对,根据键可以快速检索到对应的值
- 为什么要有hashCode?:以HashSet如何检查重复为例进行说明,当把对象加入到HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有则HashSet会假设对象没有重复出现,如果发现相同hashcode值得对象则会继续调用equals方法来检查hashcode相等的对象是否真的相同,如果两者相同那么HashSet就不会让其加入,如果不同则会重新散列到其他位置,这样就大大减少了equals的次数和大大提高了执行速度
- 如果两个对象相等,则hashcode一定相等
- 两个对象相等,对两个分别调用equals方法都返回true
- hashcode相等的两个对象不一定是相等的
- 因为equals相等的两个对象的hashcode一定要也相等,所以equals方法被重写则hashcode方法也必须被重写
20181203
1、面向对象与面向过程的区别
面向对象:易维护、易复用、易扩展,且封装、继承、多态的特性使得可以设计出低耦合的系统,系统更加灵活和易于维护,但是性能比面向过程低
面向过程:性能比面向对象高,这是因为面向对象中类调用需要实例化而导致开销比较大,消耗资源,诸如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是重要的因素,但是没有面向对象易维护、易复用、易扩展
2、Java语言特点
- 面向对象(封装、继承、多态)
- 平台无关性(JVM虚拟机)
- 支持多线程(C++语言没有内置的多线程机制,必须通过操作系统的多线程功能实现多线程程序设计,而java语言直接提供多线程支持)
- 支持网络编程
- 编译与解释并存
3、JDK\JRE\JVM
- JDK:java开发工具包,程序开发者使用,包括JRE以及其他供开发者使用的工具包,用以编译、调试程序
- JRE:java运行环境,用以运行java程序
- JVM:负责将字节码转换成特定机器码,且提供了内存管理、垃圾回收和安全机制等;
=》JDK用以开发,JRE用以运行;JDK&JRE均包含JVM;JVM是java编程语言的核心且具有平台独立性
4、字节码
Java源代码=》经过 编译器 编译后 =》变成 jvm可执行的字节码(即虚拟指令)=》jvm 将每一条要执行的字节码 =》 送给 解释器 => 解释器将其翻译成特定机器上可执行的机器码 => 程序运行
供虚拟机理解的代码称之为字节码(.class文件),不面向任何特定的处理器,只面向虚拟机
优点:java语言通过字节码方式,一定程序上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点 =》因此,java程序运行时比较高效,且字节码并不是专对一种特定的机器使得java程序无需重新编译即可在不同计算机上运行
5、Java VS C++
- 均是面向对象语言(封装、继承、多态)
- 指针:java不提供指针来直接访问内存,程序内存更加安全
- 多继承:java的类是单继承的,C++支持多继承,但是java接口可以多继承
- 内存模型:java有自动内存管理机制,不需要程序员手动释放无用内存
6、java程序主类:应用程序主类 VS 小程序主类
一个java程序可以有多个类,但是只能有一个类是主类;主类是java程序执行的入口点
在java应用程序中,主类是指包含main()方法的类,不一定是public类
在java小程序中,主类是一个继承自系统类JApplet或Applet的子类,且必须是public类
7、java应用程序 VS java小程序
- 应用程序是从主线程启动的(即main()方法)
- 小程序没有main方法,主要是嵌在浏览器页面上运行(调用init线程或者run()来启动)
8、字符型常量 VS 字符串常量
- 形式上:字符型常量由单引号引起的一个字符,字符串常量由双引号引起的若干个字符
- 含义上:字符型常量=一个整型值(ASCII值),可以参加表达式运算;字符串常量代表一个地址值,即是该字符串在内存中存放的位置
- 占内存大小:字符型常量只占2个字节,字符串常量占若干个字节(至少一个字符结束标志)(char在java中占两个字节)
9、重写override VS 重载overload
- 重写override:父子类中,方法名、参数列表(参数类型、个数、顺序)必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符大于等于父类(=>父类方法访问修饰符为private则子类不能重写该方法)
- 重载overload:同一类中,方法名必须相同,参数列表(参数类型、个数、顺序)不同,方法返回值和访问修饰符可以不同,发生在编译时
=>构造器Constructor是否可被重写override?:不能,在继承时父类的私有属性和构造方法是不能被继承的,所以Constructor也就不能被重写override,但是可以被重载overload即一个类中有多个构造函数
10、面向对象三大特性-封装、继承、多态
- 封装:把一个对象的属性私有化,并提供一些可以被外界访问的方法
- 继承:将已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但是不能够选择性地继承父类!
=》子类拥有父类非private的属性和方法
=》子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
=》子类可以用自己的方式去实现父类的方法(重写override)
- 多态:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时时不确定的,而是在程序运行期间才能够确定,即一个引用变量到底会指向哪个类的实例对象、该引用变量发出的方法调用到底是哪个类中实现的方法,都必须在由程序运行期间才能确定
11、String VS StringBuffer VS StringBuilder,且为什么String是不可变的?
- 可变性
String类中使用final关键字修饰字符数组用以保存字符串,即private final char value[],因此String对象不然是不可变的
而StringBuffer和StringBuilder,均继承自AbstractStringBuilder类,且该类也是使用字符数组用以存储字符串的,即char[] value,没有final修饰符,因此是可变的
- 线程安全性
String中对象是不可变的,即可认为是常量,因此是线程安全的;
AbstractStringBuilder类是StringBuffer和StringBuilder的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法;StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,因此是线程安全的;而StringBuilder并没有对方法进行加同步锁,因此是非线程安全的
- 性能
每次对String类型进行改变的时候,都会生产一个新的对象,然后将引用指向新的String对象;而StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用;相同情况下,使用StringBuilder比使用StringBuffer仅能获得10%~15%左右的性能提升,但却要冒多线程不安全的危险
- 结论:
操作少量的数据 = String
单线程操作字符串缓冲区下操作大量数据 = StringBuilder
多线程操作字符串缓冲区下操作大量数据 = StringBuffer
说明:本JavaGuide系列博客为来源于https://github.com/Snailclimb/JavaGuide
等学习网站或项目中的知识点,均为自己手打键盘系列且内容会根据继续学习情况而不断地调整和完善!