JAVA基础语法
Java基础语法
- 人机交互方式
- Java语言的主要特性
- Java两种核心机制
- JDK,JRE,JVM三者之间的关系和主要结构有哪些
- Java环境变量的设置
- Java的注释类型
- java的关键字和保留字
- java标识符定义和命名规范
- java变量的定义和分类
- 自动类型转换和强制类型转换
- Java中的运算符
- 程序的流程控制
- java中的数组
- 数组和集合的互相转换
- Java类和对象
- Java对象的内存分配
- 类的成员
- 成员变量和局部变量的区别
- 面向对象特性
- java访问修饰符有哪些,作用是什么
- this和super关键字的使用
- 对象的创建过程
- 类的初始化过程
- 方法重载和重写的区别
- Object类的使用
- String类的使用
- String和StringBuffer和StringBuilder的区别
- 包装类的使用
- static关键字的使用
- 单例设计模式的两种实现
- final关键字的使用
- 抽象类和抽象方法
- Java中的接口
- 异常描述与异常体系结构
人机交互方式
图形化界面(Graphical User Interface GUI)这种方式简单直观,使用者易于接受,容易上手操作
命令行方式(Command Line Interface CLI):需要有一个控制台,输入特定的指令,让计算机完成一些操作。较为麻烦,需要记录住一些命令
Java语言的主要特性
纯粹面向对象的程序设计语言:Java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。
分布式:Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段
健壮:Java的强类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择
安全:Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。如:安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查。
体系结构中立:Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件),然后可以在实现这个Java平台的任何系统中运行。
解释型:如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统的解释器中运行。
性能略高:与那些解释型的高级脚本语言相比,Java的性能还是较优的。
原生支持多线程:在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建
跨平台:只要在需要运行 java 应用程序的操作系统上,先安装一个Java虚拟机 (JVM Java
Virtual Machine) 即可。由JVM来负责Java程序在该系统中的运行
Java两种核心机制
Java虚拟机 (Java Virtal Machine)
- JVM是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器。
- 对于不同的平台,有不同的虚拟机。
- 只有某平台提供了对应的java虚拟机,java程序才可在此平台运行
- Java虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”
垃圾收集机制 (Garbage Collection)
- 不再使用的内存空间应回收—— 垃圾回收。
- 在C/C++等语言中,由程序员负责回收无用内存。
- Java 语言消除了程序员回收无用内存空间的责任:它提供一种系统级线程跟踪存储空间的分配情况。并在JVM空闲时,检查并释放那些可被释放的存储空间。
- 垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预。
JDK,JRE,JVM三者之间的关系和主要结构有哪些
Java Development Kit Java开发工具包
Java Runtime Environment Java运行环境
Java Virtual Machine Java虚拟机
JDK = JRE + Java的开发工具(javac.exe,java.exe,javadoc.exe)
JRE = JVM + Java核心类库
Java环境变量的设置
我的电脑--属性--高级系统设置--环境变量
JAVA_HOME = bin的上一层目录
path = %JAVA_HOME%\bin
java -version检查是否安装成功
Java的注释类型
/**
* 文档注释-类
* @author Administrator
* @date 2021-10-24
*/
public class CoreTest {
/**
* 方法注释-方法
* @param a
* @param b
* @return
* @throws Exception
*/
public int add(int a,int b) throws Exception{
// 单行注释
/*
*多行注释
*/
return a+b;
}
}
java的关键字和保留字
关键字keyword:
定义:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)
特点:关键字中所有字母都为小写
保留字reserved word:
现有Java版本尚未使用,但以后版本可能会作为关键 字使用。自己命名标识符时要避免使用这些保留字goto 、const
private | 一种访问控制方式:私用模式 |
protected | 一种访问控制方式:保护模式 |
public | 一种访问控制方式:共用模式 |
abstract | 表明类或者成员方法具有抽象属性 |
class | 类 |
extends | 表明一个类型是另一个类型的子类型,这里常见的类型有类和接口 |
final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变 |
implements | 表明一个类实现了给定的接口 |
interface | 接口 |
native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 |
new | 用来创建新实例对象 |
static | 表明具有静态属性 |
strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 |
synchronized | 表明一段代码需要同步执行 |
transient | 声明不用序列化的成员域 |
volatile | 表明两个或者多个变量必须同步地发生变化 |
break | 提前跳出一个块 |
continue | 回到一个块的开始处 |
return | 从成员方法中返回数据 |
do | 用在do-while循环结构中 |
while | 用在循环结构中 |
if | 条件语句的引导词 |
else | 用在条件语句中,表明当条件不成立时的分支 |
for | 一种循环结构的引导词 |
instanceof | 用来测试一个对象是否是指定类型的实例对象 |
switch | 分支语句结构的引导词 |
case | 用在switch语句之中,表示其中的一个分支 |
default | 默认,例如,用在switch语句中,表明一个默认的分支 |
try | 尝试一个可能抛出异常的程序块 |
catch | 用在异常处理中,用来捕捉异常 |
throw | 抛出一个异常 |
throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
import | 表明要访问指定的类或包 |
package | 包 |
boolean | 基本数据类型之一,布尔类型 |
byte | 基本数据类型之一,字节类型 |
char | 基本数据类型之一,字符类型 |
double | 基本数据类型之一,双精度浮点数类型 |
float | 基本数据类型之一,单精度浮点数类型 |
int | 基本数据类型之一,整数类型 |
long | 基本数据类型之一,长整数类型 |
short | 基本数据类型之一,短整数类型 |
super | 表明当前对象的父类型的引用或者父类型的构造方法 |
this | 指向当前实例对象的引用 |
void | 声明当前成员方法没有返回值 |
goto | 保留关键字,没有具体含义 |
const | 保留关键字,没有具体含义 |
java标识符定义和命名规范
定义:Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符
技巧:凡是自己可以起名字的地方都叫标识符。
定义合法标识符规则:
Ø由26个英文字母大小写,0-9 ,_或 $ 组成。
Ø数字不可以开头。
Ø不可以使用关键字和保留字,但能包含关键字和保留字。
ØJava中严格区分大小写,长度无限制。
Ø标识符不能包含空格。
Java中的名称命名规范:
Ø包名:多单词组成时所有字母都小写:xxxyyyzzz
Ø类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
Ø变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
Ø常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
l注意1:在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
l注意2:java采用unicode字符集,因此标识符也可以使用汉字声明,但是不建议使用。
java变量的定义和分类
定义:变量是内存中的一个存储区域,该区域的数据可以在同一数据类型范围内不断变化,变量是程序中基本的存储单元,包含变量类型、变量名和存储的值。
声明变量:
Ø 语法:<数据类型> <变量名称>
Ø 例如:
int var;
var = 10;
int var2 = 11;
变量的赋值
Ø 语法:<变量名称> = <值> Ø 例如:var = 10;
声明和赋值变量
Ø 语法: <数据类型> <变量名> = <初始化值> Ø 例如:int var = 10;
使用变量注意:
ØJava中每个变量必须先声明,后使用
Ø使用变量名来访问这块区域的数据
Ø变量的作用域:其定义所在的一对{ }内,变量只有在其作用域内才有效
Ø同一个作用域内,不能定义重名的变量
变量的分类
按数据类型(在内存中分配不同大小的内存空间):
类型 | 占用存储空间 | 表数范围 | 备注 | |
---|---|---|---|---|
整型 | byte | 1字节=8bit | -128~127 | 整型常量默认为int,声明为long需在后缀加l或L,变量通常声明为int,除非不足以表示。 |
short | 2字节 | -2的15次方~2的15次方-1 | ||
int | 4字节 | -2的31次方~2的31次方-1 | ||
long | 8字节 | -2的63次方~2的63次方-1 | ||
浮点型 | 单精度float | 4字节 | -3.403E38 ~ 3.403E38 | Java 的浮点型常量默认为double型,声明float型常量,须后加‘f’或‘F’。 |
双精度double | 8字节 | -1.798E308 ~ 1.798E308 | ||
字符型 | 2字节 | 通常意义字符。Unicode编码 | ||
布尔型 | 取值true或者false | 4字节 | java虚拟机中没有任何供boolean值专用的字节码指令,Java语言表达所操作的boolean值,在编译之后都使用java虚拟机中的int数据类型来代替:true用1表示,false用0表示 |
按作用域:
在方法体外,类体内声明的变量称为成员变量
在方法体内,内部声明的变量称为局部变量,局部变量除形参外,都需要显式初始化。
自动类型转换和强制类型转换
自动类型转换:容量小的类型自动转换为容量大的数据类型。
数据类型按容量大小排序为:
①有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
②byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。Boolean类型不能与其它数据类型运算。
③当把任何基本数据类型的值和字符串(String)进行连接运算时(+),基本数据类型的值将自动转化为字符串(String)类型。
强制类型转换:自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。
①使用时要加上强制转换符:(),但可能造成精度降低或溢出,格外要注意。
②boolean不可以转化为其他数据类型。
③通常,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。
对象的类型转换:
向上转型:子类转为父类可以自动转。多态的体现
向下转型:父类转化为子类必须先用instanceof进行判断,然后进行强制类型转换,无继承关系的引用类型间的转换是非法的。
Java中的运算符
①算术运算符:
②赋值运算符:
符号:=
当“=”两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理。
支持连续赋值。
扩展赋值运算符: +=, -=, *=, /=, %=
③比较运算符:结果都是boolean型,也就是要么是true,要么是false。
④逻辑运算符: 用于连接布尔型表达式
⑤位运算符:直接对整数的二进制进行的运算
⑥三元运算符
(条件表达式)?表达式1:表达式2;
条件表达式为true,运算结果为表达式1的结果。
条件表达式为false,运算结果为表达式2的结果。
⑦运算符的优先级
程序的流程控制
流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。
其流程控制方式采用结构化程序设计中规定的三种基本流程结构,即:
①顺序结构:程序从上到下逐行地执行,中间没有任何判断和跳转。
②分支结构:根据条件,选择性地执行某段代码。
if(条件表达式1){ //语句块只有一条执行语句时,一对{}可以省略,但建议保留。
代码块1
}else if(条件表达式2){
代码块2
}....
else{
代码块n
}
switch(表达式){
case(表达式值1):
代码段1;
break;
case(表达式值2):
代码段2;
break;
...
case(表达式值n):
代码段n;
break;
default:
代码段n+1;
break;
}
switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);
case子句中的值必须是常量,不能是变量名或不确定的表达式值。
同一个switch语句,所有case子句中的常量值互不相同。
break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有break,程序会顺序执行到switch结尾。
default子句是可任选的。同时,位置也是灵活的。当没有匹配的cas时,执行default.
如果判断的具体数值不多,而且符合byte、short 、char、int、String、枚举等几种类型。虽然两个语句都可以使用,建议使用swtich语句。因为效率稍高。
其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。也就是说,使用switch-case的,都可以改写为if-else。反之不成立。
③循环结构:根据循环条件,重复性的执行某段代码。
循环语句的四个组成部分:
Ø初始化部分(init_statement)
Ø循环条件部分(test_exp)
Ø循环体部分(body_statement)
Ø迭代部分(alter_statement)
⑴For循环结构
⑵while循环
⑶do..while循环
⑷break用于跳出循环
⑸continue用于跳过其所在循环的一次执行。
Continue只能使用在循环结构中。
⑹return表示结束一个方法,当方法执行到return语句时,表示该方法结束。
与break和continue不同的是,return直接结束整个方法,不管这个return处于多少层循环之内。
java中的数组
定义:数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址。
数组的长度一旦确定,就不能修改。
我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
一位数组的使用:
声明方式:
type var[]或type[] var
例如:int a[] int[] a1 double b[] String[] c
数组初始化:
动态初始化:数组声明且为数组元素分配空间与赋值操作分开
例如:int[] arr = new int[3]; String names[];
arr[0] = 3; names =new String[3];
arr[1] = 6; names[0]=“aa”;
arr[2] = 9; names[1]=“bb”;
names[2]=“cc”;
静态初始化:在定义数组的同时就为数组元素分配空间并赋值。
int arr[] = new int[]{3,9,8};
String names[] = {"a","b","c"}
定义并用运算符new为之分配空间后,才可以引用数组中的每个元素;
数组元素的引用方式:数组名[数组元素下标] 数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 —>n-1;如int a[]=new int[3]; 可引用的数组元素为a[0]、a[1]、a[2]
每个数组都有一个属性length指明它的长度,例如:a.length 指明数组a的长度(元素个数) Ø数组一旦初始化,其长度是不可变的。
数组是引用类型,它的元素相当于类的成员变量,因此数组一经分配空间,其中的每个元素也被按照成员变量同样的方式被隐式初始化。
int a[]= new int[5];
System.out.println(a[3]); //a[3]的默认值为0
//对于基本数据类型而言,默认初始化值各有不同
//byte:0;short:0;int:0;long:0L;float:0.0F;double:0.0;boolean:false;char:0。
//对于引用数据类型而言,默认初始化值为null(注意与0不同!)
数组工具类Arrays的sort排序用法:
① sort[arr[] a] 对数组进行排序 由低到高
② sort[arr[] a,Comparator
public static void main(String[] args) {
Integer[] a = {1, 4, 6, 3, 2, 9, 5};
Arrays.sort(a, ((o1, o2) -> o1 - o2));
System.out.println(Arrays.toString(a));//[1, 2, 3, 4, 5, 6, 9] 升序,默认的
Arrays.sort(a, ((o1, o2) -> o2 - o1));
System.out.println(Arrays.toString(a));//[9, 6, 5, 4, 3, 2, 1] 降序
}
数组和集合的互相转换
数组转集合:必须是对象类型的数组,不能是基本类型。基本类型的要先转化为包装类型
@Test
public void toList(){
Integer[] a = {1, 4, 6, 3, 2, 9, 5};
//通过Arrays.asList
List<Integer> list = Arrays.asList(a);
//得到的集合无法增加和删除元素,底层是Arrays下的内部类。可以如下写法:把Arrays.asList()的结果作为构造器的参数传递给任何Collection。
List<Integer> newList = new ArrayList<>(list);
//通过流式操作
List<Integer> streamList = Arrays.stream(a).collect(Collectors.toList());
}
集合转数组:
@Test
public void toLArray() {
Integer[] a = {1, 4, 6, 3, 2, 9, 5};
List<Integer> streamList = Arrays.stream(a).collect(Collectors.toList());
Integer[] arrays = streamList.toArray(new Integer[streamList.size()]);
}
Java类和对象
定义:类是对一类事物的描述,是抽象的、概念上的定义。对象是实际存在的该类事物的一个个体,是类的实例(instance)。
Java用类class来描述类。
类的成员有以下:
类的语法格式:
修饰符 class 类名 {
属性声明;
方法声明;
}
Java类的实例化,即创建类的对象:
Ø创建对象语法: 类名 对象名 = new 类名();
Ø使用“对象名.对象成员”的方式访问对象成员(包括属性和方法)。
在同一个类中,类的方法可以直接访问类的成员(例外:static方法无法访问非static,编译不通过),在不同类中,需要创建类的实例对象,通过对象访问类的成员。
我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。Ø如:new Person().shout();
使用情况:
Ø如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
Ø我们经常将匿名对象作为实参传递给一个方法调用。
Java对象的内存分配
Java虚拟机的内存大致分为三个区域:
栈stack:
存放:基本数据类型变量、对象的引用、线程执行方法的基本信息。
栈是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等),每个方法执行的相关调用都在栈里面;
栈是线程私有的,不能线程共享;
虚拟机会为每个线程创建一个独立的栈;
栈的存储特性类似于子弹的弹夹,先进去的后出来,后进去的先出来,如方法一调用方法二,方法二调用方法三,先开辟方法一的栈帧,再开辟方法二的栈帧,最后开辟方法三的栈帧,但是执行的关闭顺序是先关闭方法三的栈帧,再关闭方法二的栈帧,最后关闭方法一的栈帧,而当整个线程执行完时,该线程的栈空间就会被关闭;
栈由系统自动分配,是一个连续的内存空间。
堆heap:
存放:对象、数组
堆是用于存储创建好的对象和数组(数组也是对象);
堆空间是唯一的,被所有线程共享;
堆是一个不连续的内存空间,分配灵活,速度慢。
每当我们new一个对象的时候,都会在堆里面有一个该对象的创建。
方法区method area:
存放:类的加载信息、静态变量、字符串常量
方法区实际上也在堆里面,但是相较于其他比较特殊,所以单独提出来。
方法区也叫静态区,且是唯一的,被所有线程共享;
方法区实际上也是堆,只是用于存储类的代码信息(如类信息的[Class对 象],静态变量,字符串常量等),即用来存放程序中永远是不变或唯一的内容;
java虚拟机执行过程:
public class Test {
public static void main(String[] args) {
Person p = new Person();
p.name = "zhangsan";
p.age = 18;
p.say();
}
}
class Person {
String name;
int age;
public void say() {
System.out.println("hahaha");
}
}
JVM运行一个class文件时,类加载器将Test类加载到方法区,将main方法入栈。
栈中运行main方法中看到Person类,将Person类信息加载到方法区,看到p会在栈中开辟一块内存空间,看到new Person()在堆内存中开辟空间,将堆中该空间地址赋值给p。并会指向方法区该对象所属的类的地址。加载到给对象赋值的阶段,通过p指向的地址去堆中给相应属性赋值,加载到方法调用时,去堆中找到p指向的地址对象,根据对象指向的方法区的地址找到所调用的方法,将该方法压入栈中,调用完毕后将该方法出栈。Main方法运行结束出栈。
类的成员
①属性:
定义:访问修饰符 数据类型 属性名 = 初始化值 ;
例子:
public static final Integer age = 18;
属性有生命周期。
分为:成员变量(声明在方法体外)和局部变量(声明在方法体内部)
属性赋值的顺序:
默认初始化->显式初始化->多个初始化块按顺序依次执行->构造器中初始化->对象.属性名赋值
②方法:
声明格式:
修饰符 返回值类型 方法名(参数类型 形参1, 参数类型 形参2, ….){
方法体程序代码
return 返回值;
}
方法参数的传递:
值传递,基本数据类型传递值,引用数据类型传递地址
注意:
方法被调用一次,就会执行一次。
没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
定义方法时,方法的结果应该返回给调用者,交由调用者处理。
方法中只能调用方法或属性,不可以在方法内部定义方法。
方法的重载(overload):
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
重载的特点:
与返回值类型无关,方法名相同,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
可变个数的形参的方法:
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String…books);
说明:
\1. 声明格式:方法名(参数的类型名 ...参数名)
\2. 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
\3. 可变个数形参的方法与同名的方法之间,彼此构成重载
\4. 可变参数方法的使用与方法参数部分使用数组是一致的
\5. 方法的参数部分有可变形参,需要放在形参声明的最后
\6. 在一个方法的形参位置,最多只能声明一个可变个数形参
递归方法:
一个方法中调用了方法自身。
递归方法中必须有跳出方法的判定,否则就构成无穷递归。
③构造器:
构造器的特征:
- Ø它具有与类相同的名称
- Ø它不声明返回值类型。(与声明为void不同)
- Ø不能被static、final、synchronized、abstract、native修饰,不能有
- return语句返回值
构造器的作用:创建对象;给对象进行初始化
语法:
修饰符 类名 (参数列表) {
初始化语句;
}
注意:
- Java语言中,每个类都至少有一个构造器。
- 默认构造器的修饰符与所属类的修饰符一致。
- 一旦显式定义了构造器,则系统不再提供默认构造器。
- 一个类可以创建多个重载的构造器。
- 父类的构造器不可被子类继承。
④代码块:
对java内部的类或者对象进行初始化,代码块如果有修饰符,只能被static修饰,称为静态代码块,未被static修饰称为非静态代码块。
静态代码块:用static 修饰的代码块。
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行一次。
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
⑤内部类
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。分为成员内部类和局部内部类
成员内部类作为类的成员的角色:
和外部类不同,Inner class还可以声明为private或protected;可以调 用外部类的结构。Inner class 可以声明为static的,但此时就不能再使 用外层类的非static的成员变量;
成员内部类作为类的角色:
可以在内部定义属性、方法、构造器等结构。可以声明为abstract类 ,因 此可以被其它的内部类继承。可以声明为final的 编译以后生成 OuterClass$InnerClass.class字节码文件(也适用于局部内部类)
【注意】
\1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员。
\2. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
\3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据
\4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
局部内部类:声明在方法或者代码块中
class 外部类{
方法(){
class 局部内部类{ }
}
{
class 局部内部类{ }
}
}
只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型。
局部内部类可以使用外部类的成员,包括私有的。局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。局部内部类和局部变量地位类似,不能使用public,protected,缺省,private。局部内部类不能使用static修饰,因此也不能包含静态成员。
匿名内部类:
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
格式:
new 父类构造器(实参列表)| 实现接口(){
//匿名内部类的类体部分
}
匿名内部类的特点
- Ø 匿名内部类必须继承父类或实现接口
- Ø 匿名内部类只能有一个对象
- Ø 匿名内部类对象只能使用多态形式引用
成员变量和局部变量的区别
成员变量 | 局部变量 | |
---|---|---|
声明的位置 | 方法体外,类中 | 方法形参、方法内部、代码块内、构造方法内 |
修饰符 | Public、static、final、private、Protected、默认 | 不能有修饰符 |
初始化值 | 有默认初始化值 | 没有默认初始化值、必须显示初始化后才能调用 |
内存加载位置 | 堆内存中 | 栈内存中 |
面向对象特性
①封装:
定义:Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节;
- 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
- 便于修改,增强代码的可维护性;
②继承:
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
类继承语法规则:
class Subclass extends SuperClass
继承的出现减少了代码冗余,提高了代码复用性,让类与类产生了关系,是多态的前提。子类继承父类,也就继承了父类的属性和方法。子类可以使用父类的属性和方法,也可以自己创建新的属性和方法。子类不是父类的子集,而是对父类的扩展。
规则:
子类不能直接访问父类的私有的成员变量和方法。
一个子类只能有一个父类,支持单继承和多层继承。一个父类可以派生多个子类。
方法的重写:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限。子类不能重写父类中声明为private权限的方法。
- 子类方法抛出的异常不能大于父类被重写方法的异常。
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
③多态
父类的引用指向子类对象
不同子类继承父类,重写父类同名方法,在调用父类该方法时,执行子类重写后的方法实现。
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)。多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法) “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)。
对象的多态:在Java中,子类的对象可以替代父类的对象使用。一个变量只能有一种确定的数据类型,一个引用类型变量可能指向(引用)多种不同类型的对象
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。
方法的形参为父类类型,实参可以传递一个子类的对象。
虚拟方法调用(多态情况下) 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
子类继承父类,若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。
instanceof 操作符:检验某个对象x是否属于类A的对象,返回值类型为boolean。
java访问修饰符有哪些,作用是什么
this和super关键字的使用
this:
当一个对象创建后,Java虚拟机(JVM)就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this。因此,this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现this,并且this只和特定的对象关联,而不和类关联,同一个类的不同对象有不同的this。
1、使用this来区分当前对象。
Java中为解决变量的命名冲突和不确定性问题,引入关键字this代表其所在方法的当前对象的引用:
\1) 构造方法中指该构造器所创建的新对象;
\2) 方法中指调用该方法的对象;
\3) 在类本身的方法或构造器中引用该类的实例变量(全局变量)和方法。
this只能用在构造器或者方法中,用于获得调用当前的构造器方法的对象引用。可以和任何的对象引用一样来处理这个this对象。
说明:
(1)当实例变量和局部变量重名,JAVA平台会按照先局部变量、后实例变量的顺序寻找。即,方法中使用到的变量的寻找规律是先找局部变量,再找实例变量。如果没用找到,将会有一个编译错误而无法通过编译。
(2)如果使用this.a,则不会在方法(局部变量)中寻找变量a,而是直接去实例变量中去寻找,如果寻找不到,则会有一个编译错误。
(3)在一个方法内,如果没有出现局部变量和实例变量重名的情况下,是否使用this关键字是没有区别的。
(4)在同一个类中,Java普通方法的互相调用可以省略this+点号,而直接使用方法名+参数。因为Java编译器会帮我们加上。
2、 在构造器中使用this来调用对象本身的其他构造器。
在构造器中使用this([args_list]);可以调用对象本身的其他的构造器。直接使用this()加上类构造器所需要的参数。就可以调用类本身的其他构造器了。如果类中有多个其他构造器定义,系统将自动根据this()中的参数个数和类型来找出类中相匹配的构造器。
注意: 在构造器中可以通过this()方式来调用其他的构造器。但在一个构造器中最多只能调用一个其他的构造器。并且,对其他构造器的调用动作必须放在构造器的起始处(也就是构造器的首行),否则编译的时候将会出现错误,另外不能在构造器以外的地方以这种方式调用构造器。
3、 this关键字还有一个重大的作用就是返回类的引用。如在代码中,可以使用return this来返回某个类的引用。此时,这个this关键字就代表类的名称。
super:
在Java类中使用super来调用父类中的指定操作:
Øsuper可用于访问父类中定义的属性
Øsuper可用于调用父类中定义的成员方法
Øsuper可用于在子类构造器中调用父类的构造器 l
注意:尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员。super的追溯不仅限于直接父类。super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。
Super用在子类构造器中时:子类中所有的构造器默认都会访问父类中空参数的构造器。 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行。如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。
子类不管有多少个构造器,都会直接或者间接通过super调用父类的构造器,进而调用父类的父类的构造器,直到调用了object的空参构造器。
虽然调用了多个父类的构造器,但自始至终只创建了一个子类对象。
对象的创建过程
(1)子类产生对象并对成员变量赋默认值初始化
(2)对构造方法中的形参赋值
(3)构造方法中是否有this调用,有的话一直追溯到没有this的构造方法,期间对追溯到的构造方法形参赋值。
(4)Super方法显式或隐式调用父类的构造方法,父类构造方法对方法执行234步的判断,直到追溯到object类。
(5)父类对当前类中的成员变量显式初始化
(6)执行父类的构造方法中的代码,一直往下重复到一级父类。
(7)对子类中的成员变量进行显式初始化。
(8)执行子类当前构造方法中的代码。
类的初始化过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题。准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。 l
初始化:执行类构造器
什么时候会发生类初始化?
类的主动引用(一定会发生类的初始化)
当虚拟机启动,先初始化main方法所在的类
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java.lang.reflect包的方法对类进行反射调用
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(不会发生类的初始化)
当访问一个静态域时,只有真正声明这个域的类才会被初始化
当通过子类引用父类的静态变量,不会导致子类初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
方法重载和重写的区别
重载的特点:与返回值类型无关,方法名相同,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
重写的特点:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
\1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表。
\2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
\3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限。子类不能重写父类中声明为private权限的方法。
\4. 子类方法抛出的异常不能大于父类被重写方法的异常。
从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”; 而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
Object类的使用
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类
所有类都继承了object类,也就继承了object类的方法
对象常用方法如下:
何时需要重写equals()?
当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。
结论:复写equals方法的时候一般都需要同时复写hashCode方法。通常参与计算hashCode的对象的属性也应该参与到equals()中进行计算。
重写equals方法的原则:
对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返 回是“true”。 l
自反性:x.equals(x)必须返回是“true”。 l
传递性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。 l
一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。 l
任何情况下,x.equals(null),永远返回是“false”; x.equals(和x不同类型的对象)永远返回是“false”。
重写 hashCode() 方法的基本原则:
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法的返回值也应相等。
对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
以Eclipse/IDEA为例,在自定义类中可以调用工具自动重写equals和hashCode。
问题:为什么用Eclipse/IDEA复写hashCode方法,有31这个数字?
选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)并且31只占用5bits,相乘造成数据溢出的概率较小。31可以 由i*31== (i<<5)-1来表示,现在很多虚拟机里面都有做相关优化。(提高算法效率)31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结果只能被素数本身和被乘数还有1来整除!(减少冲突)
toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。在进行String与其它类型数据的连接操作时,自动调用toString()方法
String类的使用
String类代表字符串,是一个常量final类,代码不可变的字符序列。它们的值在创建后不可修改,内存存储在字符数组value[]中。字符串常量池在方法区。
//字符串引用直接指向字符串常量池
String s1 = "javaEE";
String s2 = "javaEE";
//字符串引用指向堆中字符串对象,对象中的value指向字符串常量池
String s3 = new String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false
Person p1 = new Person();
p1.name = "tom";
Person p2 = new Person();
p2.name = "tom";
System.out.println(p1.name .equals( p2.name)); //true
System.out.println(p1.name == p2.name); //true
System.out.println(p1.name == "tom"); //true
字符串拼接:
常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
只要其中有一个是变量,结果就在堆中.
如果拼接的结果调用intern()方法,返回值就在常量池中.
String a1 = "hello";
String a2 = "world";
String a3 = "hello" + "world";
String a4 = a1 + "world";
String a5 = a1 + a2;
String a6 = (a1 + a2).intern();
a3 == a4 //false
a4 == a5 //false
a3 == a5 //false
a3 == a6 //true
字符串常用方法:
int length():返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符 return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将String中的所有字符转换为小写
String toUpperCase(): 使用默认语言环境,将String中的所有字符转换为大写
String trim():返回字符串的副本,忽略前导空白和尾部空白
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar 替换此字符串中出现的所有 oldChar 得到的
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement):使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串
String replaceFirst(String regex, String replacement):使用给定的replacement替换此字符串匹配给定的正则表达式的第一个子字符串。
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
字符数组转字符串:
ØString 类的构造器:String(char[]) 和 String(char[],int offset,int length) 分别用字符数组中的全部字符和部分字符创建字符串对象。
字符串转字符数组:
Øpublic char[] toCharArray():将字符串中的全部字符存放在一个字符数组中的方法。
Øpublic void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):提供了将指定索引范围内的字符串存放到数组中的方法。
字节数组转字符串:
String(byte[]):通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
String(byte[],int offset,int length) :用指定的字节数组的一部分,即从数组起始位置offset开始取length个字节构造一个字符串对象。 l
字符串转字节数组:
public byte[] getBytes() :使用平台的默认字符集将此 String 编码为byte序列,并将结果存储到一个新的 byte 数组中。
public byte[] getBytes(String charsetName) :使用指定的字符集将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。
String和StringBuffer和StringBuilder的区别
StringBuffer和StringBuilder代表可变字符序列,可对字符串内容进行增删,此时不会新增对象。作为参数传递时,方法内部可以改变值。其对象必须使用构造函数生成
ØStringBuffer():初始容量为16的字符串缓冲区
ØStringBuffer(int size):构造指定容量的字符串缓冲区
ØStringBuffer(String str):将内容初始化为指定字符串内容
String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。
当对字符串进行修改的时候,特别是字符串对象经常改变的情况下,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
==和equals的区别
1 == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址。
2 equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
3 具体要看自定义类里有没有重写Object的equals方法来判断。
4 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
包装类的使用
针对八种基本数据类型定义相应的引用类型—包装类(封装类),有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
// 基本类型转包装类
Integer i = new Integer(2);
Integer i2 = Integer.valueOf(3);
Integer i3 = 3;
// 包装类转基本类型
int a = i.intValue();
int a1 = i2;
//包装类型转字符串
String s1 = i.toString();
String s2 = Integer.toString(i2);
//基本类型转字符串
String s3 = String.valueOf(3);
String s4 = "" + 3;
//字符串转包装类
Integer i4 = new Integer("1");
Integer i5 = Integer.valueOf("3");
//字符串转基本类型
int b1 = Integer.parseInt("22");
int b2 = new Integer("3");
static关键字的使用
在Java类中,可用static修饰属性、方法、代码块、内部类。
被修饰后的成员具备以下特点:
Ø随着类的加载而加载
Ø优先于对象存在
Ø修饰的成员,被所有对象所共享
Ø访问权限允许时,可不创建对象,直接被类调用
①修饰的属性为类变量,被该类的所有成员所共享。
②修饰的方法可以用 类名.方法名() 的方式调用,在static修饰的方法内部, 只能访问由static修饰的类的属性或者方法,不能访问类的非static结构。
被static修饰的类的方法不能被重写,方法内部不能有this和super。
③修饰代码块,为静态代码块,在类加载的时候执行一次。
单例设计模式的两种实现
由于单例模式只创建一个实例,减少了系统的性能开销
/**
* 饿汉式
*/
class Single1 {
//内部提供一个当前类的实例,该实例也必须是static的
private static Single1 instance = new Single1();
//私有化构造器
private Single1() {
}
//提供公共静态方法获取该实例
public static Single1 getInstance() {
return instance;
}
}
/**
* 懒汉式
*/
class Single2 {
//内部提供一个当前类的实例,为static但不初始化
private static Single2 instance;
//私有化构造方法
private Single2() {
}
//提供公共静态方法获取该实例
//如果不加synchronized的话是线程不安全的
public synchronized static Single2 getInstance() {
if (null == instance) {
instance = new Single2();
}
return instance;
}
}
final关键字的使用
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
final标记的类不能被继承。提高安全性,提高程序的可读性。
比如:String类、System类、StringBuffer类
final标记的方法不能被子类重写。
比如:Object类中的getClass()。
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。
final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。
final double MY_PI = 3.14;
抽象类和抽象方法
Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
用abstract关键字来修饰一个类,这个类叫做抽象类。用abstract来修饰一个方法,该方法叫做抽象方法。
抽象方法:只有方法的声明,没有方法的实现。以分号结束:比如:public abstract void talk(); 含有抽象方法的类必须被声明为抽象类。
抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
不能用abstract修饰变量、代码块、构造器;
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
虽然抽象类不能被实例化,但可以有构造函数。由于抽象类的构造函数在实例化派生类之前发生,所以,可以在这个阶段初始化抽象类字段或执行其它与子类相关的代码。
/**
* 抽象类的定义
*/
abstract class AbstractClass {
public String name;
//抽象方法
public abstract void say();
//实例方法
public void eat() {
System.out.println("eat food");
}
public AbstractClass() {
this.name = "tom";
}
}
/**
* 子类继承抽象类,必须实现抽象类的抽象方法
*/
class SubClass extends AbstractClass {
@Override
public void say() {
System.out.println("say chinese");
}
}
Java中的接口
接口(interface)是抽象方法和常量值定义的集合。
接口的特点:
- 用interface来定义。
- 接口中的所有成员变量都默认是由public static final修饰的。
- 接口中的所有抽象方法都默认是由public abstract修饰的。
- 接口中没有构造器。
- 接口采用多继承机制。
定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口。
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。
接口和抽象类的关系对比:
JDK8.0对接口的改进
增加了静态方法和默认方法,
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法。
若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接口时,会出现:接口冲突。解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
若一个类继承了一个父类和实现了一个接口,接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有相同名称和参数的默认方法会被忽略。
异常描述与异常体系结构
Java程序执行过程中发生的不正常情况称为异常。开发过程中的语法错误和逻辑错误不是异常。
Java异常结构体系:
异常事件分为:
Error:
Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
Exception:
其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。捕获异常最佳时间为编译期,但有的错误只在运行时发生,异常分类:编译时异常和运行时异常。
1.运行时异常
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
2.编译时异常
是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。对于这类异常,如果程序不处理,可能会带来意想不到的结果。
Java异常处理机制:
方式一:try-catch-finally
try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}[ finally{ //finally可有可无
...... //无论是否发生异常,都无条件执行的语句
} ]
如果异常是RuntimeException类或是它的子类,这些类的异常的特点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过( 但运行时会发生异常使得程序运行终止 )。
如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为运行时异常。
方式二:throws + 异常类型
如果一个方法中的语句执行时可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。子类重写父类方法抛出异常的声明的类型不能比父类的异常范围大。
Java异常类的对象除了在程序执行的时候出现异常时由java虚拟机抛出,也可以根据需要人工创建并抛出。首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。
IOException e = new IOException();
throw e; Ø
可以抛出的异常必须是Throwable或其子类的实例。
下面的语句在编译时将会产生语法错误:
throw new String("want to throw");
用户自定义异常类:
一般地,用户自定义异常类都是RuntimeException的子类。
- 自定义异常类通常需要编写几个重载的构造器。 l
- 自定义异常需要提供serialVersionUID。
- 自定义的异常通过throw抛出。
- 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。
本文来自博客园,作者:New悠悠我心,转载请注明原文链接:https://www.cnblogs.com/wsndlzs/p/16253207.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具