目录
构造器 Constructor 是否可被 override:
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是:
在一个静态方法内调用一个非静态成员为什么是非法的,静态方法和实例方法有何不同:
WeakReference与SoftReference的区别:
String 和 StringBuffer、StringBuilder 的区别是什么 String 为什么是不可变的:
反射中,Class.forName和classloader的区别:
写在前面的话:
这一篇文章的内容大致了解一下Java的一些基本内容,文章结构可能比较混乱!!!
后面的文章将会一步一步的进行系统的记录!!!
就是玩嘛!!!
/*
输出字符串:Hello world !
主方法入口:main方法(快捷:psvm+回车)
*/
public static void main (String[] args) {
//输出语句(快捷:sout+回车)
System.out.println("Hello world !");
}
知识图解:
应用程序 = 算法 + 数据结构
一、Java基础概念:
Java历史简述:
-
1991 年Sun公司的James Gosling(詹姆斯•高斯林)等人开始开发名称为 Oak的语言,希望专攻计算机在家电产品上的嵌入式应用(如电视机顶盒、面包烤箱、移动电话等)。
-
1995年将Oak语言更名为Java;
-
2009年,甲骨文公司宣布收购Sun公司。
Java语言的特点:
- 面向对象(封装,继承,多态)
- 平台无关性( Java 虚拟机实现平台无关性,一次编译,到处运行)
- 简单易学(与C语言的面向过程相比,Java的面向对象更接近人的语言习惯)
- 安全性,可靠性(Java中没有指针,程序员无法直接操作内存,而是把操作权限交给Java虚拟机,使程序不容易出现不容易出现内存泄漏和内存溢出问题。)
- 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 java的lang包提供一个Thread类本身就支持多线程)
- 编译与解释并存(Java编译生成字节码文件,交给Java虚拟机解释)
面向对象与面向过程对比:
Java核心思想就是面向对象,万物皆对象!!!
面向过程:
- 优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
- 缺点: 没有面向对象易维护、易复用、易扩展。
面向对象:
- 优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
- 缺点: 性能比面向过程低。
Java的三个版本:
- JavaSE:即Java标准版,主要用于开发和部署桌面。(C/S架构)
- JavaEE:即Java企业版,主要针对企业应用的开发。(B/S架构)
- JavaME:即Java微型版,主要针对移动设备和嵌入式设备。
注:从JDK 5.0开始 J2EE 改名为 java EE,J2SE 改名为 java SE,J2ME 改名成 java ME。
JDK、JRE、JVM之间的区别与关系:
三者之间的区别:
- JDK:(Java Development Kit)即java的开发与运行环境,他除了包含完整的JRE之外,还包含了供开发者使用的工具包。
- JRE:(Java Runtime Environment)即Java运行环境,非开发者只需要安装 JRE来运行程序, 它包含java运行的所需的类库+JVM(java虚拟机)。
- JVM: (Java Virtual Machine) 即Java虚拟机, 当我们运行一个程序时,JVM 负责将字节码转换为特定机器代码,JVM 提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统,正是 java 程序可以一次编写多处执行的原因。
三者之间的关系:
- JDK中包含Java开发工具包,同时也包含了JRE。
- 运行已经开发好的Java程序,只需要安装JRE。
- JVM和JRE的关系:JRE包含了JVM,JVM是运行Java程序的核心虚拟机,同时也包含了Java程序所需的环境支持
总结:JDK>JRE>JVM
Java环境变量:
环境变量的意义:
让java bin目录下的工具,可以在任意目录下运行,原理是将该工具所在目录告诉了系统,当使用该工具时,由系统帮我们去找指定的目录。
JAVA_HOME:
它指向jdk的安装目录,引用%JAVA_HOME%即可,避免每次引用都输入很长的路径串,方便第三方软件引用约定好的JAVA_HOME变量,保证程序正常运行。
Path环境变量:
设置Path环境变量之后就可以在任何目录下执行javac/java等工具命令了。 系统默认先去当前路径下找要执行的程序,如果没有,再去path中设置的路径下找。
ClassPath:
如果指定了classpath,那么会在指定的目录下查找要运行的类文件(JDK1.5后不需要配置)
javac命令和java命令做什么事情呢:
java运行分两部分:一个是编译,一个是运行。
- javac:负责的是编译的部分,当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译。编译后生成class文件。
- java:负责运行的部分.会启动jvm虚拟机,加载运行时所需的类库,并对class文件进行执行
一个文件要被执行,必须要有一个执行的起始点,这个起始点就是main函数.
什么是字节码,采用字节码的好处是什么:
Java文件类型,一共有两种:
- 扩展名为Java,Java的源文件,编译之前的纯文本文件,用来储存Java源代码。
- 扩展名为class,Java的类文件,编译之后的二进制文件,存储的是字节码。
编译后的.class文件存储就是字节码。
采用字节码的最大好处: 可以实现一次编译多平台运行,也就是java的与平台无关性,它依靠不同平台的Java虚拟机将编译后的字节码解释成具体平台上的机器指令执行。
import java和javax有什么区别:
刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来说使用。然而随着时间的推移,javax 逐渐的扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包将是太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准API的一部分。
所以,实际上java和javax没有区别。这都是一个名字。
Java和C++的区别:
- 都是面向对象的语言,都支持封装、继承和多态
- Java 不提供指针来直接访问内存,程序内存更加安全
- Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
- Java 有自动内存管理机制,不需要程序员手动释放无用内存
二、Java数据类型:
基本数据类型:
引用类型:
类、接口类型、数组类型、枚举类型、注解类型
区别:
- 基本数据类型 在被创建时,在栈上给其划分一块内存,将数值直接存储在栈上。
- 引用数据类型 在被创建时,首先要在栈上给其引用(句柄)分配一块内存,而对象的具体信息都存储在堆内存上,然后由栈上面的引用指向堆中对象的地址。
三、Java访问权限修饰符:
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
-
default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
-
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
-
public : 对所有类可见。使用对象:类、接口、变量、方法
-
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
四、字符型常量和字符串常量的区别:
- 形式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符。
- 含义上: 字符常量相当于一个整形值( ASCII含义上: 字符常量相当于一个整形值( ASCII 值),可以参加表达式运算 字符串常量代表一个地址值(该字符串在内存中存放位置)。
- 占内存大小:字符常量只占2个字节 字符串常量占若干个字节(至少一个字符结束标志) (注意: char在Java中占两个字节)
Java关键字、保留字:
Java标识符:
五、Java面向对象:
Java 面向对象编程三大特性:
- 封装
- 继承
- 多态
封装:
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。
继承:
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码 同时继承也为实现多态做了铺垫。
在 Java 中,一个类可以由其他类派生。如果你要创建一个类,而且已经存在一个类具有你所需要的属性或方法,那么你可以将新创建的类继承该类。
利用继承的方法,可以重用已存在类的方法和属性,而不用重写这些代码。被继承的类称为超类(super class),派生类称为子类(subclass)。
关于继承:
- 子类拥有父类非 private 的属性和方法。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
多态:
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
体现: 父类引用或者接口的引用指向了自己的子类对象
代码中如何实现多态(三种方式):
- 接口实现
- 继承父类重写方法
- 同一类中进行方法重载
多态有什么好处:
允许不同类对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)。主要有以下优点:
- 可替换性:多态对已存在代码具有可替换性
- 可扩充性:增加新的子类不影响已经存在的类结构
- 接口性:多态是超类通过方法签名,向子类提供一个公共接口,由子类来完善或者重写它来实现的。
接口和抽象类的区别:
- 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),抽象类可以有非抽象的方法。
- 接口中的实例变量默认是 final 类型的,而抽象类中则不一定。
- 一个类可以实现多个接口,但最多只能实现一个抽象类。
- 一个类实现接口的话要实现接口的所有方法,而抽象类不一定。
- 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象 从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
![](https://i-blog.csdnimg.cn/blog_migrate/7265a071680858b042a2e50222b73ac3.png)
接口的意义抽象类的意义:
接口的意义:接口的意义用三个词就可以概括:规范,扩展,回调。
抽象类的意义:为其他子类提供一个公共的类型,封装子类中重复定义的内容,定义抽象方法,子类虽然有不同的实现,但是定义时一致的
重载和重写的区别:
- 重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
- 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
一个类的构造方法的作用是什么,若一个类没有声明构造方法,该程序能正确执行吗?为什么?
主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
构造方法有哪些特性:
- 名字与类名相同
- 没有返回值,但不能用void声明构造函数;
- 生成类的对象时自动执行,无需调用。
构造器 Constructor 是否可被 override:
在讲继承的时候我们就知道父类的私有属性和构造方法并不能被继承,所以 Constructor 也就不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是:
帮助子类做初始化工作
在 Java 中定义一个不做事且没有参数的构造方法的作用:
Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
在一个静态方法内调用一个非静态成员为什么是非法的,静态方法和实例方法有何不同:
为什么非法:
由于静态方法可以不通过对象进行调用,类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
有何不同:
- 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
- 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制.
在一个类的静态成员中去访问其非静态成员之所以会出错是
因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错
成员变量与局部变量的区别有那些:
- 从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰;
- 从变量在内存中的存储方式来看,成员变量是对象的一部分,而对象存在于堆内存,局部变量存在于栈内存。
- 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
- 成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情况例外被 final 修饰的成员变量也必须显示地赋值);而局部变量则不会自动赋值。
什么是不可变对象:
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。
java 创建对象的几种方式:
- 采用new(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)
- 通过反射
- 采用clone
- 通过序列化机制
前2者都需要显式地调用构造方法。造成耦合性最高的恰好是第一种,因此你发现无论什么框架,只要涉及到解耦必先减少new的使用。
Object中有哪些公共方法:
-
equals()
-
clone()
-
getClass()
-
notify(),notifyAll(),wait()
-
toString
-
finalize()
java当中的四种引用:
强引用,软引用,弱引用,虚引用。不同的引用类型主要体现在GC上:
- 强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。如obj.equels(new Object());而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放
- 软引用(SoftReference):在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
SoftReference<String> softRef = new SoftReference<String>(s); // 再创建一个软引用关联该对象
s = null; // 消除强引用,现在只剩下软引用与其关联,该String对象为软可达状态
s = softRef.get(); // 重新关联上强引用
- 弱引用(WeakReference):具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象。
- 虚引用(PhantomReference):顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
WeakReference与SoftReference的区别:
这点在四种引用类型中已经做了解释,这里简单说明一下即可:
虽然 WeakReference 与 SoftReference 都有利于提高 GC 和 内存的效率,但是 WeakReference ,一旦失去最后一个强引用,就会被 GC 回收,而软引用虽然不能阻止被回收,但是可以延迟到 JVM 内存不足的时候。
为什么要有不同的引用类型:
不像C语言,我们可以控制内存的申请和释放,在Java中有时候我们需要适当的控制对象被回收的时机,因此就诞生了不同的引用类型,可以说不同的引用类型实则是对GC回收时机不可控的妥协。有以下几个使用场景可以充分的说明:
-
利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题.
-
通过软引用实现Java对象的高速缓存:比如我们创建了一Person的类,如果每次需要查询一个人的信息,哪怕是几秒中之前刚刚查询过的,都要重新构建一个实例,这将引起大量Person对象的消耗,并且由于这些对象的生命周期相对较短,会引起多次GC影响性能。此时,通过软引用和 HashMap 的结合可以构建高速缓存,提供性能。
对象的相等与指向他们的引用相等,两者有什么不同:
对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们指向的内存地址是否相等。
什么是方法的返回值,返回值在类的方法里的作用是什么:
方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!
String 和 StringBuffer、StringBuilder 的区别是什么 String 为什么是不可变的:
可变性:
简单的来说:String 类中使用 final 关键字字符数组保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的
线程安全性:
String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
性能:
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
三者总结:
- 操作少量的数据 -> String
- 单线程操作字符串缓冲区下操作大量数据 -> StringBuilder
- 多线程操作字符串缓冲区下操作大量数据 -> StringBuffer
== 与 equals(重要):
- == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型“= =”比较的是值,引用数据类型 = = 比较的是内存地址).
- equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
说明:
String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。
当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。
hashCode 与 equals(重要):
hashCode()介绍:
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)。
为什么要有 hashCode:
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head fist java》第二版)。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
hashCode()与equals()的相关规定:
- 如果两个对象相等,则hashcode一定也是相同的。
- 两个对象相等,对两个对象分别调用equals方法都返回true。
- 两个对象有相同的hashcode值,它们也不一定是相等的。
- 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖,hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等。
六、Java多线程:
简述线程,程序、进程的基本概念。以及他们之间关系是什么?
- 新建(new):新创建了一个线程对象。
- 可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取cpu的使用权。
- 运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。
- 阻塞(block):**阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有 机会再次获得cpu timeslice转到运行(running)状态。
- 阻塞的情况分三种:
- (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放 入等待队列(waitting queue)中。
- (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
- (三). 其他阻塞: 运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
- 死亡(dead):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
final, finally, finalize的区别:
final:
- 修饰类 表示该类不能被继承
- 修饰方法 表示该方法不能被重写
- 修饰基本类型变量 表示该变量只能被赋值一次,如果修饰引用,那么表示引用不可变,引用指向的内容可变。
- 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
- 被final修饰的常量,在编译阶段会存入常量池中。
finally:
finally 是用于异常处理的场面,无论是否有异常抛出,都会执行
finalize:
finalize是Object的方法,所有类都继承了该方法。 当一个对象满足垃圾回收的条件,并且被回收的时候,其finalize()方法就会被调用
七、Java异常处理:
祖先java.lang包中的 Throwable类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。
- Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
- Exception(异常):是程序本身可以处理的异常。 Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
- Throwable类常用方法
- 1. public string getMessage():返回异常发生时的详细信息
- 2. public string toString():返回异常发生时的简要描述
- 3. public void printStackTrace():在控制台上打印Throwable对象封装的异常信息
- 异常处理总结:
- try 块: 用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
- catch 块:用于处理try捕获到的异常。
- finally 块: 无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行
- 在以下4种特殊情况下,finally块不会被执行:
- 在finally语句块中发生了异常。
- 在前面的代码中用了System.exit()退出程序。
- 程序所在的线程死亡。
- 关闭CPU。
最常见到的runtime exception与Error:
- NullPointerException (空指针异常)
- ArrayIndexOutOfBoundsException(数组下标越界)
- IllegalArgumentException (参数错误)
- ArithmeticException 算术异常,比如除数为零
- ClassCastException 类型转换异常
Error:
- OutOfMemoryError (堆内存溢出)
- StackOverflowError(栈内存溢出)
八、Java文件流:
Java中所有的流都是基于字节流,所以最基本的流是:字节流
- 输入输出字节流
InputStream OutputStream - 字符流
在字节流的基础上,封装了字符流
Reader Writer - 缓存流
进一步,又封装了缓存流
BufferedReader PrintWriter - 数据流
DataInputStream DataOutputStream - 对象流
ObjectInputStream ObjectOutputStream
获取用键盘输入常用的的两种方法:
方法一:通过Scanner:
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
方式二:通过BufferedReader:
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
九、Java反射:
什么是Java反射:
编译期和运行期,编译期就是编译器帮你把源代码翻译成机器能识别的代码,比如编译器把java代码编译成jvm识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行,JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
Java反射机制的底层原理实现:
Object 类,是所有Java 类的继承根源,其内声明了数个应该在所有Java 类中被改写的方法:其中getClass()返回一个Class 对象,而这个Class 类十分特殊。它和一般类一样继承自Object,当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。
Class类其中的方法:
获取公共构造器 getConstructors()
获取所有构造器 getDeclaredConstructors()
获取该类对象 newInstance()
获取类名包含包路径 getName()
获取类名不包含包路径 getSimpleName()
获取类公共类型的所有属性 getFields()
获取类的所有属性 getDeclaredFields()
获取类公共类型的指定属性 getField(String name)
获取类全部类型的指定属性 getDeclaredField(String name)
获取类公共类型的方法 getMethods()
获取类的所有方法 getDeclaredMethods()
获得类的特定公共类型方法: getMethod(String name, Class[] parameterTypes)
获取内部类 getDeclaredClasses()
获取外部类 getDeclaringClass()
获取修饰符 getModifiers()
获取所在包 getPackage()
获取所实现的接口 getInterfaces()
创建反射的三种方式:
- 直接通过类名点.class获取
- 通过Object类的getClass方法来获取 通过Object类的getClass方法来获取
- 通过全类名获取用的比较多推荐使用 例如:Class.forName(“com.mysql.jdbc.Driver”)
反射中,Class.forName和classloader的区别:
java中class.forName()和classLoader都可用来对类进行加载。
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
什么是序列化,序列化有什么好处:
什么是序列化:
简单说就是将内存中的对象保存下来(将对象的内容进行流化),可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间,并且可以把保存的对象状态再读出来。实现java序列化的手段是让该类实现接口 Serializable,这个接口是一个标识性接口,没有任何方法,仅仅用于表示该类可以序列化。
什么情况下需要使用序列化:
- 把的内存中的对象保存到一个文件中或者数据库中时候;
- 用序列化在网络上传送对象的时候;
- 通过RMI传输对象的时候;
注意事项:
当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
把一个对象完全转成字节序列,方便传输。
Java序列话中如果有些字段不想进行序列化 怎么办:
对于不想进行序列化的变量,使用transient关键字修饰。
transient关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。transient只能修饰变量,不能修饰类和方法。
Java拷贝:
浅拷贝:
只复制一个对象,对象内部存在的指向其他对象数组或者引用则不复制
深拷贝:
深拷贝:对象,对象内部的引用均复制
拷贝的几种方式:
- System.arraycopy(浅拷贝)
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
通过源代码我们可以看到,关键字native说明它不是用java语言写的,而是调用其他语言的代码。
- Arrays.copyOf(浅拷贝):实际上它调用的就是System.arraycopy.
- Object.clone:clone()比较特殊,对于对象而言,它是深拷贝,但是对于数组而言,它是浅拷贝。