分类 标签 存档 社区 博客 友链 GitHub 订阅 搜索

Java 基础知识

158 浏览

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() 方法,则该线程结束生命周期,死亡的线程不可再次复生

jpeg

备注: 可以用早起坐地铁来比喻这个过程:

还没起床: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 等学习网站或项目中的知识点,均为自己手打键盘系列且内容会根据继续学习情况而不断地调整和完善!

评论  
留下你的脚步
推荐阅读