一Java基础
Posted 一只小熊猫呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一Java基础相关的知识,希望对你有一定的参考价值。
本章概要
- 异常的分类及处理
- 异常的概念
- 异常的分类
- 处理异常的方式
- 反射机制
- 动态语言的概念
- 反射机制的概念
- 反射的作用
- Java 的反射 API
- 反射的过程
- 创建对象的两种方式
- Method 的 invoke 方法
1.2 异常的分类及处理
1.2.1 异常的概念
异常指在方法不能按正常方式完成时,可以通过抛出异常的方式退出该方法。在异常中封装了方法执行过程中的错误信息及原因,调用方在获取该异常后可根据业务的情况选择处理该异常或继续抛出该异常。
在方法执行中出现异常时,Java 异常处理机制会将代码的执行权交给异常处理器,异常处理器根据在系统中定义的异常处理规则执行不同的异常处理逻辑(抛出异常或捕捉并处理异常)。
1.2.2 异常的分类
在 Java 中,Throwable 是所有错误或异常的父类,Throwable 又可分为 Error 和 Exception,常见的 Error 有 AWTError、ThreadDeath,Exception 又可分为 RuntimeException(运行时异常)和 CheckedException(检查异常),如下:
Error 指 Java 程序运行错误。如果程序在启动时出现 Error,则启动失败;如果程序在运行过程中出现 Error,则系统将退出进程。出现 Error 通常是因为系统的内部错误或资源耗尽,Error 不能在运行过程中被动态处理。如果程序出现 Error,则系统能做的工作也只能是记录错误的成因和安全终止。
Exception 指 Java 程序运行异常,即运行中的程序发生了人们不期望发生的事件,可以被 Java 异常处理机制处理。Exception 也是程序开发中异常处理的核心,如图:
- RuntimeException:指在 Java 虚拟机正常运行期间抛出的异常。RuntimeException 可被捕获并处理,如果出现 RuntimeException,那么一定是程序发生错误导致的。我们通常需要抛出该异常或捕获并处理该异常。常见的 RuntimeException 有 NullPointerException、ClassCastException、ArrayIndexOutOfBundsException 等。
- CheckedException:Java 编译器在编译阶段会检查 CheckedException 异常并强制程序捕获和处理此类异常,即要求程序在可能出现异常的地方通过 try catch 语句块捕获并处理异常。常见的 CheckedException 有由于 I/O 错误导致的 IOExceptioin、SQLException、ClassNotFoundException 等。该异常一般由于打开错误的文件、SQL 语法错误、类不存在等引起。
1.2.3 处理异常的方式
处理异常有抛出异常和使用 try catch 语句块捕获并处理异常这两种方式。
(1)抛出异常:指遇到异常时不进行具体处理,而是将异常抛给调用者,由调用者根据情况处理。有可能是直接捕获并处理,也有可能是继续向上层抛出异常。抛出异常有三种方式:throws、throw、系统自动抛出异常。其中,throws 作用在方法上,用于定义方法可能抛出的异常;throw 作用在方法内,表示明确抛出一个异常。
throw 和 throws 的具体区别如下:
- 位置不同:throws 作用在方法上,和面跟着的是异常的类;而 throw 作用在方法内,后面跟着的是异常的对象。
- 功能不同:throws 用于声明方法在运行过程中可能出现的异常,以便调用者根据不同的异常类型预先定义不同的处理方式;throw 用于抛出封装了异常信息的对象,程序在执行到 throw 时后续的代码将不再执行,而是跳转到调用者,并将异常信息抛给调用者。也就是说,throw 后面的语句将无法被执行(finally 语句块除外)。
(2)使用 try catch 语句块捕获并处理异常:使用 try catch 语句块捕获异常能够有针对地处理每种可能出现的异常,并在捕捉到异常后根据不同的情况做不同的处理。
相关面试题:
- Java 中的异常处理方式(机制)有哪些?★★★☆☆
- Error 和 Exception 的区别是什么?★★☆☆☆
- throw 和 throws 的具体区别是什么?★☆☆☆☆
1.3 反射机制
Java 的反射机制可以动态获取类和对象的信息,以及动态调用对象的方法,被广泛应用于动态代理的场景中。
1.3.1 动态语言的概念
动态语言指程序在运行时可以改变其结构的语言,比如新的属性或方法的添加、删除等结构上的变化。javascript 、Ruby 、 Python 等都属于动态语言;C、C++ 不属于动态语言。从反射的角度来说,Java 属于半动态语言。
1.3.2 反射机制的概念
反射机制指在程序运行过程中,对任意一个类都能获取其所有属性和方法,并且对任意对象都能调用其任意方法。这种动态获取类和对象的信息,以及动态调用对象的方法的功能被称为 Java 的反射机制。
1.3.3 反射的作用
Java 中的对象有两种类型:编译时类型和运行时类型。编译时类型指在声明对象时采用的类型,运行时类型指为对象赋值时所采用的的类型。
在如下代码中,persion 对象的编译时类型为 Persion ,运行时类型为 Student ,因此无法在编译时获取在 Student 类中定义的方法:
Persion persion = new Student();
因此,程序在编译期间无法预知该对象和类的真实信息,只能通过运行时信息来发现该对象和类的真实信息,而其真实信息(对象的属性和方法)通常通过反射机制来获取,这便是 Java 中反射机制的核心功能。
1.3.4 Java 的反射 API
Java 的反射 API 主要用于在运行过程中动态生成类、接口或对象等信息,其常用 API 如下:
- Class 类:用于获取类的属性、方法等信息
- Field 类:表示类的成员变量,用于获取和设置类中的属性值
- Method 类:表示类的方法,用于获取方法的描述信息或执行某个方法
- Constructor 类:表示类的构造方法
1.3.5 反射的过程
反射的步骤如下:
- 获取想要操作的类的 Class 对象,该 Class 对象时反射的核心,通过它可以调用类的任意方法。
- 调用 Class 对象所对应的类中定义的方法,这是反射的使用阶段
- 使用反射 API 类来获取并调用类的属性和方法等信息。
获取 Class 对象的 3 种方式如下:
- 调用某个对象的 getClass 方法以获取该类对应的 Class 对象:
Persion p = new Persion();
Class clazz = p.getClass();
- 调用某个类的 class 属性以获取该类对应的 Class 对象:
Class clazz = Persion.class;
- 调用 Class 类中的 forName 静态方法以获取该类对应的 Class 对象,这是最安全、性能也最好的方式:
Class clazz = Class.forName("fullClassPath"); // fullClassPath 为类的包路径及名称
我们在获得想要操作类的 Class 对象后,可以通过 Class 类中的方法获取并查看该类中的方法和属性,具体的实例代码如下:
// 1.获取 Persion 类的 Class 对象
Class clazz = Class.forName("hello.java.reflect.Persion");
// 2.获取Persion 类的所有方法的信息
Method[] method = clazz.getDeclaredMethods();
for(Method m : method)
System.out.println(m.toString);
// 3.获取 Persion 类的所有成员的属性信息
Field[] field = clazz.getDeclaredFields();
for(Field f : field)
System.out.println(f.toString);
// 4.获取 Persion 类的所有构造方法的信息
Constructor[] constructor = clazz.getDeclaredConstructors();
for (Constructor c : constructor)
System.out.println(c.toString);
1.3.6 创建对象的两种方式
创建对象的两种方式如下:
- 使用 Class 对象的 newInstance 方法创建该 Class 对象对应类的实例,这种方式要求该 Class 对象对应的类有默认的空构造器。
- 先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance 方法创建 Class 对象对应类的实例,通过这种方式可以选定构造方法创建实例。
创建对象的具体代码如下:
//1.1:获取 Persion 类的 Class 对象
Class clazz = Class.forName("hello.java.reflect.Persion");
//1.2:使用 newInstance 方法创建对象
Persion p = (Persion) clazz.newInstance();
//2.1:获取构造方法并创建对象
Constructor c = clazz.getDeclaredConstructor(String.class,String.class,int.class);
//2.2:根据构造方法创建对象并设置属性
Persion p1 = (Persion) c.newInstance("李四","男","20");
1.3.7 Method 的 invoke 方法
Method 提供了关于类或接口上某个方法及如何访问该方法的信息,那么在运行代码中如何动态调用该方法呢?答案是通过调用 Method 的 invoke 方法实现。通过 invoke 方法可以实现动态调用,比如可以动态传入参数并将方法参数化。具体过程:获取 Method 对象,并调用 Method 的invoke 方法,如下所述:
- 获取 Method 对象:通过调用 Class 对象的 getMethod(String name,Class<?> … parameterTypes) 返回一个 Method 对象,它描述了此 Class 对象所表示的类或接口指定的公共成员方法。name 参数是 String 类型,用于指定所需方法的名称。parameterTypes 参数是按声明顺序标识该方法的形参类型的 Class 对象的一个数组,如果 parameterTypes 为 null,则按空数组处理。
- 调用 Method 的 invoke 方法:通过调用 Method 对象的 invoke 方法来动态执行函数。invoke 方法的具体使用代码如下:
//1.获取 Persion 类(hello.java.reflect.Persion)的 Class 对象
Class clz = Class.forName("hello.java.reflect.Persion");
//2.获取 Class 对象中的 setName 方法
Method method = clz.getMethod("setName", String.class);
//3.获取 Constructor 对象
Constructor constructor = clz.getConstructor();
//4.根据 Constructor 定义对象
Object object = constructor.newInstance();
//5.调用 Method 的 invoke 方法,这里的 Method 表示 setName 方法
//因此,相当于动态调用 object 对象的 setName 方法并传入 alex 参数
method.invoke(object,"alex");
以上代码首先通过 Class.forName 方法获取 Persion 类的 Class 对象,然后调用 Persion 类的 Class 对象的 getMethod(“setName”,String.class) 获取一个 Method 对象;接着使用 Class 对象获取指定的 Constructor 对象并调用 Constructor 对象的 newInstance 方法创建 Class 对象对应类的实体;最后调用 method.invoke 方法实现动态调用,这样就通过反射动态生成类的对象并调用其方法。
相关面试题:
- Java 反射机制的作用是什么?★★★☆☆
- Java 反射机制创建对象的方式有哪些?★★☆☆☆
- Java 是如何实现动态调用某个方法的?★☆☆☆☆
- 通过 Java 反射创建对象和通过 new 创建对象,哪个效率更高?★☆☆☆☆
- 除了可以使用 new 方法创建对象,还可以使用什么方法创建对象?★☆☆☆☆
Java基础总结一(概述基础知识)
文章目录
Java基础总结一(概述、基础知识)
Java概述
什么是Java
Java是一门面向对象的编程语言。
JVM、JRE、JDK的关系
JVM:Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。
JRE:Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包。
JDK:Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等。
什么是跨平台性?原理是什么
指Java一次编写,到处运行。实现原理:Java程序通过Java虚拟机在系统平台上运行的,只要该系统可以安装相应的Java虚拟机,该系统就可以运行Java程序。
什么是字节码?采用字节码的最大好处是什么
字节码
Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。
采用字节码的好处
Java语言通过字节码的方式,在一定程度上解决了传统解释性语言执行效率低的问题,同时又保存了解释语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不是专门对一种特定的机器,因此,Java程序无需重新编译就可以在多种不同的计算机上运行。
Oracle JDK和Open JDK的对比
- Oracle JDK版本将每三年发布一次,而Open JDK版本每三个月发布一次.
- Open JDK 是一个参考模型并且是完全开源的,而Oracle JDK是Open JDK的一个实现,并不是完全开源的。
- Oracle JDK 比 Open JDK 更稳定。Open JDK和Oracle JDK的代码几乎相同,但Oracle JDK有更多的类和一些错误修复。因此,如果您想开发企业/商业软件,建议您选择Oracle JDK,因为它经过了彻底的测试和稳定。某些情况下,有些人提到在使用Open JDK 可能会遇到了许多应用程序崩溃的问题,但是,只需切换到Oracle JDK就可以解决问题。
Java基础知识
Java有哪些数据类型
基本数据类型
- 整型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
- 字符型:char(2字节)
- 浮点型:float(4字节)、double(8字节)
- 布尔型:boolean
long类型数字后要加后缀L,float要加后缀F。
引用数据类型
- 数组
- 接口
- 类
类型转换
自动类型转换:小到大
强制类型转换:大到小
表达式类型自动提升:byte 、short、char、转换为int型,表达式最终类型以表达式中类型等级最高为准。
运算符
- 算数运算符:+ - * / % ++ –
- 赋值运算符:= -= += *= /=
- 位运算符:& |^ >> << >>>
- 比较运算符:== != > < >= <=
- 逻辑运算符:&& || !
- 三目运算符:? :
Java中的取余的结果可以不是整型,如:5.2%3.1=2.1
流程控制
-
顺序
-
分支
if(){ }else{ }
switch(expr){ case expr: }
在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。从 Java5 开始,Java 中引入了枚举类型,expr 也可以是 enum 类型,从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
-
循环:while、do_while、for、for_each
-
循环控制:break、continue、return
数组
数组的定义
type[] arrayname
数组的初始化
- 静态初始化(直接给每个元素赋值)
- 动态初始化(全部赋值为0)
数组在内存中的存储
定义数组 int[] a;
数组引用变量a存储在栈内存
数组元素存储于堆内存中
数组查找:通过引用变量找到堆内存的元素
数组定义时为引用变量开辟空间(栈),初始化时为元素开辟空间(堆)
对数组的操作
Arrays类
多维数组
本质还是一维数组
面向对象
面向对象概述
面向对象和面向过程的区别
- 面向过程:优点是性能方面比面向对象高,因为类调用时需要大量实例化,开销较大。缺点是没有面向对象易维护、易复用、易扩展。
- 面向对象:由于面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活。但是性能比面向过程低
面向对象的三大特征
-
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。
-
继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承可以提高代码复用性。继承是多态的前提。
-
多态:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高了程序的拓展性。
实现多态的三大要素:继承、重写、向上转型。
什么是多态机制?
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
类和对象
类和对象的区别
类是模板,对象是实体。如:人类是类,小明是实体。
定义类
构造器
成员变量
方法
对象的产生和使用
对象的产生:先定义,在使用new关键字实例化对象
对象的使用:访问对象的变量,调用对象的方法。
对象的存储
对象的引用变量存储在栈内存中
对象存储在堆内存中
通过引用变量找到堆内存中的对象
this和super
-
this:this是自身的一个对象,代表对象本身,可以理解为指向对象本身的一个指针。
this的用法在Java中大体可以分为3种:
- 普通的直接引用,this相当于是指向当前对象本身
- 形参与成员名字重名,用this来区分
- 引用本类的构造函数
-
super:super可以理解为指向自己父类对象的一个指针,而这个父类指的是离自己最近的一个父类。
super也有三种用法:
- 普通的直接引用
- 子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分
- 引用父类构造函数
-
this和super的区别:本质上来讲,this是一个指向本对象的指针,而super是Java的关键字。
方法
Java方法和C函数的区别
Java方法不可以单独定义,方法不能单独存在,方法要不然属于类,要不然属于对象,方法不可以单独执行,必须使用类或对象作为调用者
参数传递
值传递
方法重载
一个类中有两个及两个以上同名但参数类别不同的方法
确定方法的三要素
变量
变量分类
-
局部变量:方法内变量
-
成员变量:类中方法外
局部变量和成员变量的区别
-
类中位置不同
成员变量:在方法外。
局部变量:在方法内。
-
内存中的位置不同
成员变量:在堆内存中。
局部变量:在栈内存中。
-
生命周期
成员变量:随对象的创建而存在,随对象的消失而消失。
局部变量:随方法的调用而存在,随方法的调用完毕而消失。
-
初始化
成员变量有默认初始值。
局部变量无默认初始值。
隐藏和封装
访问控制级别
- private:只可以在当前类中被访问
- default:可以被同一个类和包被访问
- protected:可以被同一个类和包以及子类访问
- public:可以被所有类访问
包
关键字:package
作用:解决类命名和文件管理问题
包机制两方面保证:源文件使用package语句指定报名,class文件必须放在对应的路径下
import:导入指定包下全部的类或者单个类
构造器
格式
- 方法名和类名相同
- 没有返回值类型
作用
创建对象时进行初始化
默认初始化
0/false/null
构造器和对象的关系
创建对象的最后一步,Jvm会收集构造方法块中的代码和构造器中的代码生成< init >方法,并执行。此时外部程序才可以使用该对象。
构造器重载
类似方法重载
类的继承
关键字
extends
特点
- 子类可以访问父类允许访问的方法和变量。
- 一个子类只有一个直接父类,可以有多个间接父类。
重写方法
发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。
包装类
定义及作用
为8种基本数据类型分别定义相应的引用
自动装箱和自动拆箱
- 装箱:将基本数据类型用他们对应的引用类型包装起来。
- 拆箱:将包装类转化为基本数据类型。
包装类型
- Boolean、Character、Byte、Short、Integer、Long、Float、Double
-128~127
如果整型字面量的值是在-128~127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象。
抽象类
关键字
abstract
抽象类的特点
抽象类中包含抽象方法,但抽象类不能创建对象
抽象方法的特点
抽象方法没有具体实现,需要子类去重写
应用场景
模板设计
接口
接口的基本概念
- 关键字:interface
- 接口定义的要求:
- 接口修饰符为public或省略
- 接口名与类名采用相同的命名规则
- 接口无构造器和初始化块
接口的成员特点
- 成员变量:默认修饰符public final static
- 成员方法:全部抽象,默认修饰符 public abstract
接口的实现
- 关键字:implements
- 特点:可以多实现
- 要求:子类必须实现接口中的所有抽象类
抽象类和接口的对比
抽象类是用来集成子类通用特性,接口时一组抽象方法的集合。
从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是对行为的抽象,是一种行为规范。
相同点
- 都不能被实例化
- 都位于继承的顶端,用于被其他类继承或者实现
- 都包含抽象方法,子类或实现类必须重写覆盖这些抽象方法
不同点
参数 | 抽象类 | 接口 |
---|---|---|
声明 | 抽象类使用abstract关键字声明 | 接口使用interface关键字声明 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现 | 使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
访问修饰符 | 抽象类中的方法可以是任意访问修饰符 | 接口方法默认修饰符是public。并且不允许定义为 private 或者 protected |
多继承 | 一个类最多只能继承一个抽象类 | 一个类可以实现多个接口 |
字段声明 | 抽象类的字段声明可以是任意的 | 接口的字段默认都是 static 和 final 的 |
内部类
什么是内部类
将一个类的定义放在另一个类的定义内部,这就是内部类。内部类本身就是类的一个属性,与其他属性定义方式一致。
内部类的分类
成员内部类、局部内部类、匿名内部类、静态内部类
静态内部类
定义在类内部的静态类,就是静态内部类
public class Outer {
static class StaticInner{
}
}
静态内部类可以访问外部类所有的静态变量,而不可以访问外部类的非静态变量,静态内部类创建方式:
Outer.StaticInner inner = new Outer.StaticInner();
成员内部类
定义在类外部,成员位置上的非静态类,就是成员内部类。
public class Outer {
class Inner{
}
}
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式如下:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
或者连起来:
Outer.Inner inner = new Outer().new Inner();
局部内部类
public class Outer {
void outMethod(){
final int a =10;
class Inner {
void innerMethod(){
System.out.println(a);
}
}
Inner inner = new Inner();
inner.innerMethod();
}
}
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义静态方法中的局部类只能访问外部类的静态变量和方法。
匿名内部类
匿名内部类是没有名字的内部类,日常开发中使用的比较多。
public class Outer {
private void test(final int i) {
new Service() {
public void method() {
for (int j = 0; j < i; j++) {
System.out.println("匿名内部类" );
}
}
}.method();
}
}
//匿名内部类必须继承或实现一个已有的接口
interface Service{
void method();
}
除了没有名字,匿名内部类还有以下特点:
- 匿名内部类必须继承一个类或者实现一个接口。
- 匿名内部类不能定义任何静态成员和静态方法。
- 当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
内部类的应用场景
- 适当使用内部类,使得代码更加灵活和富有扩展性。
- 当某个类除了它的外部类,不再被其他的类使用时。
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。
Java8中以做优化,即使不加final也不会报错
以上是关于一Java基础的主要内容,如果未能解决你的问题,请参考以下文章