Java第一章
1、Java标识符和关键字
1.1标识符
Java 中标识符是为方法、变量或其他用户定义项所定义的名称。标识符可以有一个或多个字符。在 Java 语言中,标识符的构成规则如下。
标识符的第一个符号为字母、下划线和美元符号,后面可以是任何字母、数字、美元符号或下划线。另外,Java 区分大小写,因此 myvar 和 MyVar 是两个不同的标识符。
关键字是有特殊含义的标识符,如 true、false 表示逻辑的真假。
标识符用来命名常量、变量、类和类的对象等。因此,一个良好的编程习惯要求命名标识符时,应赋予它一个有意义或有用途的名字。
1.2、关键字
关键字(或者保留字)是对编译器有特殊意义的固定单词,不能在程序中做其他目的使用。
由于 Java 区分大小写,因此 public 是关键字,而 Public 则不是关键字。但是为了程序的清晰及可读性,要尽量避免使用关键字的其他形式来命名。
2、Java注释
注释是对程序语言的说明,有助于开发者和用户之间的交流,方便理解程序。注释不是编程语句,因此被编译器忽略。
Java 支持以下三种注释方式:
2.1、单行注释
以双斜杠“//”标识,只能注释一行内容,用在注释信息内容少的地方。
2.2、多行注释
包含在“`/*`”和“`*/`”之间,能注释很多行的内容。
2.3、文档注释
包含在“`/**`”和“`*/`”之间,也能注释多行内容,一般用在类、方法和变量上面,用来描述其作用。
3、Javadoc 文档注释
Javadoc命令是用来生成自己的API文档的。
4、Java变量的声明和初始化
Java语言是强类型(Strongly Typed)语言,强类型包含以下两方面的含义:
所有的变量必须先声明、后使用。
指定类型的变量只能接受类型与之匹配的值。
常量和变量是 Java 程序中最基础的两个元素。常量的值是不能被修改的,而变量的值在程序运行期间可以被修改。
4.1、声明变量
首字符必须是字母、下划线(―)、美元符号($)或者人民币符号(¥)。标识符由数字(0~9)、大写字母(A~Z)、小写字母(a~z)、下划线(―)、美元符号($)、人民币符号(¥)以及所有在十六进制 0xc0 前的 ASCII 码组成。
不能把关键字、保留字作为标识符。
标识符的长度没有限制。
标识符区分大小写。
4.2、变量赋值
初始化变量是指为变量指定一个明确的初始值。初始化变量有两种方式:一种是声明时直接赋值,一种是先声明、后赋值。
Java 中初始化变量时需要注意以下事项:
变量是类或者结构中的字段,如果没有显式地初始化,默认状态下创建变量并默认初始值为 0。
方法中的变量必须显式地初始化,否则在使用该变量时就会出错。
5、Java变量的作用域
变量的作用域规定了变量所能使用的范围,只有在作用域范围内变量才能被使用。根据变量声明地点的不同,变量的作用域也不同。
根据作用域的不同,一般将变量分为不同的类型:成员变量和局部变量。
5.1、成员变量
ava 的成员变量有两种,分别是全局变量和静态变量(类变量)。定义在方法体和语句块之外,不属于任何一个方法,作用域是整个类。
5.2、局部变量
局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:
方法参数变量(形参)在整个方法内有效。
方法局部变量(方法内定义):从定义这个变量开始到方法结束这一段时间内有效。
代码块局部变量(代码块内定义):从定义这个变量开始到代码块结束这一段时间内有效。
局部变量在使用前必须被程序员主动初始化值。
6、Java常量
常量是指在程序的整个运行过程中值保持不变的量。在这里要注意常量和常量值是不同的概念,常量值是常量的具体和直观的表现形式,常量是形式化的表现。通常在程序中既可以直接使用常量值,也可以使用常量。
6.1、常量值
常量值又称为字面常量,它是通过数据直接表示的,因此有很多种数据类型,像整型和字符串型等。
Java 的整型常量值主要有如下 3 种形式。
十进制数形式
八进制数形式:Java 中的八进制常数的表示以 0 开头
十六进制数形式:Java 中的十六进制常数的表示以 0x 或 0X 开头
注意:这里表示字符和字符串的单引号和双引号都必须是英语输入环境下输入的符号。
6.2、定义常量
常量不同于常量值,它可以在程序中用符号来代替常量值使用,因此在使用前必须先定义。常量与变量类似也需要初始化,即在声明常量的同时要赋予一个初始值。常量一旦初始化就不可以被修改。
常量有三种类型:静态常量、成员常量和局部常量。
在定义常量时,需要注意如下内容:
在定义常量时就需要对该常量进行初始化。
final 关键字不仅可以用来修饰基本数据类型的常量,还可以用来修饰对象的引用或者方法。
为了与变量区别,常量取名一般都用大写字符。
当常量被设定后,一般情况下不允许再进行更改,如果更改其值将提示错误。
7、Java数据类型
Java语言支持的数据类型分为两种:基本数据类型(Primitive Type)和引用数据类型(Reference Type)。
7.1、Java基本数据类型
基本数据类型包括:boolean(布尔型)(1字节)、float(单精度浮点型)(4字节)、char(字符型)(2字节)、byte(字节型)(1字节)、short(短型)(2字节)、int(整型)(4字节)、long(长整型)(8字节)和 double (双精度浮点型)(8 字节)共 8 种
Java是一种强制类型的语言,所有的变量都必须先明确定义其数据类型,然后才能使用。Java中所有的变量、表达式和值都必须有自己的类型,没有“无类型”变量这样的概念。
基本数据类型又可分为 4 大类,即整数类型(包括 byte、short,int 和 long)、浮点类型(包括 float 和 double)、布尔类型和字符类型(char),下面分别介绍这 4 大类数据类型。
7.2、Java引用数据类型
引用数据类型建立在基本数据类型的基础上,包括数组、类和接口。
8、Java数据类型转换
数据类型的转换是在所赋值的数值类型和被变量接收的数据类型不一致时发生的,它需要从一种数据类型转换成另一种数据类型。数据类型的转换可以分为隐式转换(自动类型转换)和显式转换(强制类型转换)两种。
8.1、隐式转换(自动类型转换)
如果以下 2 个条件都满足,那么将一种类型的数据赋给另外一种类型变量的时,将执行自动类型转换(automatic type conversion)。
1、两种数据类型彼此兼容
2、目标类型的取值范围大于源数据类型(低级类型数据转换成高级类型数据)
当以上 2 个条件都满足时,拓宽转换(widening conversion)发生。
在运算过程中,由于不同的数据类型会转换成同一种数据类型,所以整型、浮点型以及字符型都可以参与混合运算。自动转换的规则是从低级类型数据转换成高级类型数据。
1、数值型数据的转换:byte→short→int→long→float→double。
2、字符型转换为整型:char→int。
8.2、显式转换(强制类型转换)
在强制类型转换中,如果是将浮点类型的值转换为整数,直接去掉小数点后边的所有数字;而如果是整数类型强制转换为浮点类型时,将在小数点后面补零。
9、Java算术运算符
9.1、一元运算符
Java 中的算术运算符主要用来组织数值类型数据的算术运算,按照参加运算的操作数的不同可以分为一元运算符和二元运算符。
算术一元运算一共有 3 个,分别是 -、++ 和 --。
9.2、二元运算符
Java 语言中算术运算符的功能是进行算术运算,除了经常使用的加(+)、减(-)、乘(*
)和除(\)外,还有取模运算(%)。加(+)、减(-)、乘(*
)、除(\)和我们平常接触的数学运算具有相同的含义。
算术运算符都是双目运算符,即连接两个操作数的运算符。优先级上,*
、/、% 具有相同运算级别,并高于 +、-(+、- 具有相同级别)。
10、Java赋值运算符
在 Java 语言中,“变量名称”和“表达式”内容的类型必须匹配,如果类型不匹配则需要自动转化为对应的类型。赋值运算符的优先级低于算术运算符,结合方向是自右向左;不是数学中的等号,它表示一个动作,即将其右侧的值送到左侧的变量中(左侧只允许是变量,不能是表达式或其他形式);不要将赋值运算符与相等运算符“==”混淆。
11、Java逻辑运算符
逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。
逻辑运算符是对布尔型变量进行运算,其结果也是布尔型,
&& 与 & 区别:如果 a 为 false,则不计算 b(因为不论 b 为何值,结果都为 false)
|| 与 | 区别:如果 a 为 true,则不计算 b(因为不论 b 为何值,结果都为 true)
注意:短路与(&&)和短路或(||)能够采用最优化的计算方式,从而提高效率。在实际编程时,应该优先考虑使用短路与和短路或。
关系运算符:
==:对等于
!=:不等于
>:大于
<:小于
>=:大于等于
<=:小于等于
关系运算符的结果为布尔值类型
equals(obj)方法和 == 的区别:
equals方法比较的是两个对象的内容是否相等
== 比较的是两个对象再内存中存放的地址是否为同一个
next()与nextLine()方法的区别:
1、当遇到空格或者回车的时候 next()方法停止读取
2、当遇到回车的时候 nextLine()方法停止读取,读取整行数据
单分支结构:
if () {
}
双分支结构
if语句:
if (条件表达式) {
条件为true执行
代码块
} else {
条件为false执行
}
if嵌套
* if(boolean){
* if(boolean){
* ....
* }
* }else{
* if(boolean){
…..
}
while循环:
语法:
while (循环条件) {
循环体
}
do.while循环
语法:
do{
循环体
} while (循环条件);
while循环与do...while循环的区别:
- while是先判断后执行,do...while循环是先执行然后再判断
- do...while循环至少执行一次
- while循环与do...while的特点:都是循环执行某一语句,循环次数素不固定
for循环:
语法:
for (表达式1; 表达式2; 表达式3) {
循环体
}
特点:循环次数固定
foreach语法:
for(迭代变量类型 变量的名字 :需要遍历(迭代)的对象){
语句块;
}
定义方法的语法:
修饰符 返回值类型 方法名(参数1,参数2...) { 方法体... }
return关键字的作用:1、结束方法的执行。2、将方法的执行结果返回给调用者
方法(函数)的类型:
- 无参无返回值
修饰符 void 方法名(){ 方法体 }
- 无参有返回值
修饰符 返回值类型 方法名(){ 方法体}
- 有参无返回值
修饰符 void 方法名(参数1, 参数2,....){ 方法体}
- 有参有返回值
修饰符 返回值类型 方法名(参数1, 参数2,....){ 方法体}
比较两个数的大小,将大的值返回回去
@param num1
@param num2
@return 返回最大值
定义方法:
比较两个数大小,返回布尔值
方法调用
- 通过 对象.方法名字(参数)
- 类名.方法(参数)
- 方法(参数)
调用非static关键字修饰的方法,语法:对象.方法(参数)
调用被static修饰的方法,语法:类名.方法(参数)
方法重载:
重载就是在一个类中,有相同的函数名称,但参数列表不相同的方法。
注意:
方法重载只与方法名字和参数列表有关,与方法返回值类型无关
方法签名:方法名字 + 参数列表
什么是程序?
答:程序 = 算法 + 数据结构
数组定义:
用来存放相同类型的一组数据
数组下标从0开始,对数组元素进行操作是通过数组的下标(索引)
数组一旦被创建,则数组的长度不可被修改
语法:
静态创建数组
数据类型[] 数组名 = {值1,值2....}
数据类型 数组名[] = {值1,值2....}
动态创建数组
数据类型[] 数组名 = new 数据类型[数组长度]
数据类型 数组名[] = new 数据类型[数组长度]
静态创建数组,特点:创建数组的同时给数组元素赋值
动态创建数组,特点创建数组时指定数组的长度
数组元素必须是相同类型的,不允许出现混合类型。
数组元素赋值:语法,数组名[索引] = 值
数组的长度获取方式,array.length
一、 什么是方法?
在java中,方法就是用来完成解决某件事情或实现某个功能的办法。方法实现的过程中,会包含很多条语句用于完成某些有意义的功能——通常是处理文本,控制输入或计算数值。
二、方法的定义和调用
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
方法体:方法体包含具体的语句,定义该方法的功能。
方法的作用及特点:
1、封装一段特定的业务逻辑功能
2、尽可能独立,一个方法只干一件事
3、方法可以被反复多次的调用
4、减少代码的重复,有利于代码的维护,有利于团队的协作
二维数组:
数据类型[][] 数组名字 = new 数据类型[m][n]
本质:数组的元素还是数组
访问二维数组的元素,语法:arr[x][y]
数组扩容与缩容的实质:就是创建一个新的数组,新数组的长度比原来的数组(大,扩容,小,缩容)
. 打印数组,Arrays.toString(arr)方法的作用是将数组以字符串的形式输出
用户传递一个二维数组,将其 转换为稀疏数组
面向过程的结构化程序设计:
结构化程序设计的弊端:
- 缺乏对数据的封装
- 数据与方法(操作数据)的分离
面向对象的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
什么是抽象的数据类型:
所谓抽象数据类型可以理解为:将不同类型的数据的集合组成一个整体,用来描述一种新的事务。
什么是类?什么是对象?
1、现实世界是由很多很多对象组成的
基于对象抽出了类
2、对象:真实存在的单个的个体
类:类型/类别,代表一类个体
3、类中可以包含:
3.1、所有对象所共有的属性/特征------------成员变量
3.2、所有对象所共有的行为-----------------方法
4、一个类可以创建多个对象
同一类型所创建的对象,结构相同,数据不同
5、类是对象的模板,对象是类的具体的实例
面向对象的三大特征:
封装、继承、多态
**定义一个类:**
定义类的成员变量:
类的定义包括“成员变量”的定义和“方法”的定义,其中“成员变量”用于描述对象 共同的数据结构,“方法”则是所有对象共同的行为。
引用类型变量:
为了能够实例化对象进行访问控制,需要使用一个特殊的变量——引用。引用类型变量可以存放该类对象的地址信息,通常称为“指向该类的对象”,当一个引用类型变量指向该类的对象时,就可以通过这个变量对对象实施访问。除了8种基本数据类型之外,用类、接口、数组等声明的变量都称为引用类型变量,简称“引用”。
访问对象的成员变量,调用方法:
可以通过引用访问对象的成员变量或调用方法。
方法的作用及特点
1、封装一段特定的业务逻辑功能
2、尽可能独立,一个方法只干一件事
3、方法可以被反复多次的调用
4、减少代码的重复,有利于代码的维护,有利于团队的协作
方法重载的规则:
方法名字必须相同
参数列表必须不同(参数个数不同、类型不同、参数排列顺序不同等)
方法的返回值可以相同,也可以不同(方法的重载与返回值类型无关)。
仅仅只是返回类型不同的话,则不足以称为方法的重载。
递归算法:
是一种直接或者间接地调用自身的算法。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于理解。
**递归算法:**在函数或子过程的内部,直接或者间接地调用自己的算法。
**递归算法的实质:**是把问题转化为规模缩小了的同类问题的子问题。然后递归调用函数(或过程)来表示问题的解。
**递归算法解决问题的特点:**
(1) 递归就是在过程或函数里调用自身。
(2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
(3) 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
(4) 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
**递归的两个条件:**
1、可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式。(自身调用)
2、存在一种简单情境,可以使递归在简单情境下退出。(递归出口)
递归三要素:
1、一定有一种可以退出程序的情况;
2、总是在尝试将一个问题化简到更小的规模
- 父问题与子问题不能有重叠的部分
构造方法语法结构:
构造方法是在类中定义的方法,不同于其他的方法,构造方法的定义有如下两点规则:
1)构造方法的名称必须与类名完全相同。
2)构造方法没有返回值,连void关键字有没有
构造方法的重载:
为了使用方便,可以对一个类定义多个构造方法,这些构造方法都有相同的名称,但是方法的参数列表不同,称为构造方法的重载。
一个构造方法可以通过this关键字调用另外一个重载的构造方法。
通过构造方法初始化成员变量:
构造方法常用于实例化对象和对成员变量进行初始化。
通过构造方法初始化成员变量:
构造方法常用于实例化对象和对成员变量进行初始化。
this关键字的使用:
this关键字用在方法体中,用于指向调用该方法的当前对象,简单来说,那个对象调用方法,this就指的是那个对象,严格来讲在方法中需要通过this关键字指明当前对象。
1、堆、栈、方法区:
堆:new出来的对象(包括实例变量)
栈:局部变量(包括方法的参数)
方法区:.class字节码文件(包括方法、静态变量)
3、内存区域类型
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制;
2. 堆:存放所有new出来的对象;
3. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。);
4. 静态域:存放静态成员(static定义的);
5. 常量池:存放字符串常量和基本类型常量(public static final)。有时,在嵌入式系统中,常量本身会和其他部分分割离开(由于版权等其他原因),所以在这种情况下,可以选择将其放在ROM中 ;
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。
对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
所谓静态常量池,即`*.class`文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
1、必须要关注编译期的行为,才能更好的理解常量池。
2、运行时常量池中的常量,基本来源于各个class文件中的常量池。
3、程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池。
1.内存管理:由JVM来管理的
1) 堆:
1.1) 存储new出来的对象(包括实例变量)
1.2) 垃圾: 没有任何引用所指向的对象
垃圾回收器(GC)不定时到内存中清理垃圾,
回收过程是透明的(看不到的),不一定一发现垃圾就立刻回收,
调用System.gc()可以建议虚拟机尽快调度GC来回收
1.3) 内存泄漏: 不再使用的内存没有被及时的回收
建议: 对象不再使用时及时将引用设置为null
1.4) 实例变量的生命周期:
在创建对象时存储在堆中,对象被回收时一并被回收
2) 栈:
2.1) 存储正在调用的方法中的所有局部变量(包括方法的参数)
2.2) 调用方法时,会在栈中为该方法分配一块对应的栈帧,
栈帧中存储方法中的局部变量(包括参数),
方法调用结束时,栈帧被清除,局部变量一并被清除
2.3) 局部变量的生命周期:
方法被调用时存储在栈中,方法结束时与栈帧一并被清除
3) 方法区:
3.1) 存储.class字节码文件(包括静态变量、方法)
3.2) 方法只有一份,通过this来区分具体的调用对象
引用类型数组的初始化:
1)引用类型数组的默认初始值但是null。
2)如果希望每一个元素都指向具体的对象,需要针对每一个数组元素进行new运算。
private修饰的成员变量和方法仅仅只能在本类中访问调用。
1)用protected修饰的成员变量和方法可以被子类及同一个包中的类使用。
2)默认的访问控制即不书写任何访问修饰符,默认访问控制的成员变量和方法可以被同一个包中的类调用
1)对于类的修饰可以使用public和默认的方式,public修饰的类可以被任意一个类使用,默认方法控制的类只能被同包中的类使用。
2)protected和private可以用于修饰内部类
封装的意义:
1、对外提供可调用的,稳定的功能。
2、封装容易变化的,具体的细节,外界不可访问,这样封装的意义在于:
a. 降低代码出错的可能性,便于维护。
b. 当内部的实现细节改变时,只要保证对外的功能定义不变,其他的模块就不会因此而受到牵连
封装的核心:属性私有化,行为(方法)公开化
2、关键字
1、通过extends关键字可以实现类的继承。
2、子类可以继承父类的成员变量及成员方法,同时也可以定义自己的成员变量和成员方法。
3、Java语言不支持多重继承,一个类只能继承一个父类,但是一个父类可以有多个子类。
3、继承中的构造方法
1、子类的构造方法中必须通过super关键字调用父类的构造方法,这样可以妥善的初始化继承自父类的成员变量。
2、如果子类的构造方法中没有调用父类的构造方法,Java编译器会自动的加入对父类的无参构造方法的调用(如果父类没有无参构造方法,则会有编译错误)。
4、父类的引用指向子类的对象
1、一个子类的对象可以向上造型为父类的类型,即,定义父类型的引用可以指向子类型的对象。
2、 父类的引用可以指向子类的对象,但是通过父类的引用只能访问父类所定义的成员,不能访问子类扩展的部分。
在java里面,对于super关键字通常有两种用法:
1. 用在子类的构造方法里(初始化用),主要是调用父类的默认构造方法,如果父类有不止一个构造方法,可以通过super指定具体的构造函数,比如 super(paras)。
2. 用在子类里调用隐藏或重写的属性或行为,比如 super.onDestroy()等等。
关于super 与 this 关键字的对比(区别):
1. super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)。
2. this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)。
3. super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)。
4. this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
5、调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
6、super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
7、super()和this()均需放在构造方法内第一行。
8、尽管可以用this调用一个构造器,但却不能调用两个。
9、this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
10、 this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
11、 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
方法的重写(Override):
1、发生在父子类中,方法名称相同,参数列表相同,方法体不同
2、重写方法被调用时,看对象的类型
3、遵循"两同两小一大"原则:------------了解
3.1、两同:
3.1.1、方法名称相同
3.1.2、参数列表相同
3.2、两小:
3.2.1、派生类方法的返回值类型小于或等于超类方法的
a、void时,必须相同
b、基本数据类型时,必须相同
c、引用数据类型时,小于或等于
3.2.2、派生类方法抛出的异常小于或等于超类方法的
3.3、一大:
3.3.1、派生类方法的访问权限大于或等于超类方法的
重写与重载的区别:
1、重写(Override):
1.1、发生在父子类中,方法名相同,参数列表相同,方法体不同
1.2、"运行期绑定",看对象的类型绑定方法
2、重载(Overload):
2.1、发生在一个类中,方法名相同,参数列表不同,方法体不同
2.2、"编译期绑定",看参数/引用的类型绑定方法
补充:
编译期:.java源文件,经过编译,生成.class字节码文件
运行期:JVM加载.class并运行.class
编译错误:只是检查语法
十一、多态
多态指的是同一方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但是可以指向对象的引用的类型有很多。
多态存在的条件:
1、 有继承关系
2、 子类重写父类的方法
3、 父类引用指向子类对象
多态的意义:
1、行为的多态(所有抽象方法都是多态的)
2、对象的多态(所有对象都是多态的)
多态的表现形式:
1、重写:根据对象的不同来表现多态
2、重载:根据参数的不同来表现多态
1、static修饰成员变量
1、用static修饰的成员变量不属于对象的数据结构,属于类的数据结构。
2、static变量是属于类的变量,通常可以通过类名来引用static成员。
3、static成员变量和类的信息一起存储在方法区,而不是在堆中,一个类的static变量只有一份,无论这个类创建了多少个对象。
2、static修饰方法
1、通常的方法都会涉及到对具体对象的操作,这些方法在调用时需要隐式传递对象的引用(this)。
2、static修饰的方法则不需要针对某些对象进行操作,其运行结果仅仅与输入的参数有关,调用时直接用类名引用。
3、static在调用时没有具体的对象,因此在static方法中不能对非static成员进行访问,static方法的作用在于提供一些“工具方法”和“工厂方法”等。
3、static静态块
Static块属于类的代码块,在类加载期间指向代码块,只执行一次,可以用来在软件中加载静态资源。
4、final修饰变量
1、final关键字修饰成员变量,表示变量不可被改变。
2、final修饰成员变量的两种方式初始化:
a. 声明的同时初始化
b. 构造函数中初始化
3、final关键字也可以修饰局部变量,使用之前初始化即可
5、final修饰方法
1、final关键字修饰的方法不可被重写。
2、使一个方法不能被重写的意义在于:防止子类在定义新方法使造成“不间意”重写。
6、final修饰类
1、final关键字修饰的类不可被继承。
2、 JDK中的一些基础类库被定义为final的,例如:String、Math、Integer、Double等。
3、使一个类不能继承的意义在于:可以保护类不被继承修改,可以控制滥用继承对系统造成的危害。
7、static final常量
1、static final修饰的成员变量称为常量,必须声明同时初始化,不可被改变。
2、static final常量会在编译期被替换,
抽象类描述:(在Java语言中使用abstract class来定义抽象类)
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象方法:
1、由abstract修饰
2、只有方法的定义,没有具体的实现(连{ }都没有)
如果一个方法使用 abstract 来修饰,则说明该方法是抽象方法,抽象方法只有声明没有实现。需要注意的是 abstract 关键字只能用于普通方法,不能用于 static 方法或者构造方法中。
抽象方法的 3 个特征如下:
1)抽象方法没有方法体
2)抽象方法必须存在于抽象类中
3)子类重写父类时,必须重写父类所有的抽象方法
注意:在使用 abstract 关键字修饰抽象方法时不能使用 private 修饰,因为抽象方法必须被子类重写,而如果使用了 private 声明,则子类是无法重写的。
抽象类:
1、由abstract修饰
2、包含抽象方法的类必须是抽象类
不包含抽象方法的类也可以声明为抽象类------我乐意
3、抽象类不能被实例化
4、抽象类是需要被继承的,派生类:
4.1、重写所有抽象方法--------常用
4.2、也声明为抽象类----------一般不这么做
5、抽象类的意义:
5.1、封装派生类所共有的属性和行为---------代码复用
5.2、给所有派生类提供统一的类型-----------向上造型
5.3、可以包含抽象方法,为所有派生类提供统一的入口
派生类的具体行为不同,但入口是一致的
设计规则:
1、将派生类所共有的属性和行为,抽到超类中--------抽共性
2、所有派生类的行为都一样,设计普通方法
所有派生类的行为都不一样,设计为抽象方法
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过实现(implements)接口的方式,从而来实现接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
1)一个接口可以有多个方法。
2)接口文件保存在 .java 结尾的文件中,文件名使用接口名。
3)接口的字节码文件保存在 .class 结尾的文件中。
4)接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
1)接口不能用于实例化对象。
2)接口没有构造方法。
3)接口中所有的方法必须是抽象方法。
4)接口不能包含成员变量,除了 static 和 final 变量。
5)接口不是被类继承了,而是要被类实现。
6)接口支持多继承(接口不能继承类,接口只能继承接口)。
接口特性:
1)接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
2)接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
3)接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别:
1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
注:JDK 1.8 以后,接口里可以有静态方法和方法体了。
十六、内部类
java内部类的几种类型:成员内部类,静态内部类,方法内部类,匿名内部类。
成员内部类:成员内部类是类内部的非静态类。成员内部类不能定义静态方法和变量(final修饰的除外)。这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。
成员内部类的使用方法:
1、 Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等
2、 Inner 类中定义的 test() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响,如直接访问 Outer 类中的私有属性a
3、 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );
静态内部类:
静态内部类是 static 修饰的内部类,这种内部类的特点是:
1、 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问。
2、 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与 内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员。
3、 创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
方法内部类(局部内部类):
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
需要注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。
匿名内部类:
匿名类是不能有名称的类,所以没办法引用他们。必须在创建时,作为new语句的一部分来声明他们。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口。
这种形式的new语句声明一个 新的匿名类,他对一个给定的类进行扩展,或实现一个给定的接口。他还创建那个类的一个新实例,并把他作为语句的结果而返回。要扩展的类和要实现的接口是 new语句的操作数,后跟匿名类的主体。
注意匿名类的声明是在编译时进行的,实例化在运行时进行。这意味着 for循环中的一个new语句会创建相同匿名类的几个实例,而不是创建几个不同匿名类的一个实例。
从技术上说,匿名类可被视为非静态的内 部类,所以他们具备和方法内部声明的非静态内部类相同的权限和限制。
假如要执行的任务需要一个对象,但却不值得创建全新的对象(原因可能 是所需的类过于简单,或是由于他只在一个方法内部使用),匿名类就显得很有用。匿名类尤其适合在Swing应用程式中快速创建事件处理程式。
十七、枚举
枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。枚举在曰常生活中很常见,例如一个人的性别只能是“男”或者“女”,一周的星期只能是 7 天中的一个等。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。
在 JDK 1.5 之前没有枚举类型,那时候一般用接口常量来替代。而使用 Java 枚举类型 enum 可以更贴近地表示这种常量。
1、声明枚举
声明枚举时必须使用 enum 关键字,然后定义枚举的名称、可访问性、基础类型和成员等。
任意两个枚举成员不能具有相同的名称,且它的常数值必须在该枚举的基础类型的范围之内,多个枚举成员之间使用逗号分隔。
提示:如果没有显式地声明基础类型的枚举,那么意味着它所对应的基础类型是 int。
2、枚举类
Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可。
所有枚举实例都可以调用 Enum 类的方法
## 3、为枚举添加方法
Java 为枚举类型提供了一些内置的方法,同时枚举常量也可以有自己的方法。此时要注意必须在枚举实例的最后一个成员后添加分号,而且必须先定义枚举实例。
分配一个新字符串,该字符串包含字符数组参数的子数组中的字符。
offset参数是子数组第一个字符的索引,count参数指定子数组的长度。
复制子数组的内容;字符数组的后续修改不会影响新创建的字符串。
分配一个新字符串,该字符串包含来自Unicode码点数组参数子数组的字符。
offset参数是子数组第一个编码点的索引,count参数指定子数组的长度。
子数组的内容被转换为字符;int数组的后续修改不会影响新创建的字符串。
将字符串转为int类型的值前提是字符内容必须是纯数字
使用包装类进行转换
使用concat方法进行字符串拼接
1、获取字符串长度
2、将字符串转大小写
3、去除字符串两端的空白
使用length()函数获取字符串长度
1、String字符串处理
- Java定义字符串
1
字符串是Java中特殊的关,使用方法像一般的基本数据类型,被广泛应用在java编程中, java没有内置的字符串类型,而是在标准Java类库中提供了一个String关来创建和操作字符串。
在java中定义一个学特用品筒中的方法是用双引号把它包围起来。这种用双引号提出来的一串字符实际上都是String对象,如字符串“Hello”在端译后面成为String对象,因此也可以通过创建String类的实例来定义字符串。
- 直接定义字符串
血液定义字将单是物使用双引导的、物串中的内容,例如“Hello jaw”、“Java输配”物,具体方法是用字符串剂量直接切处化一个String对象,示例如下:
String str="Hello 2ava":
String(char[]value)
分配一个新的字符串,将参数中的字符数组元素全配交为字符串。该字符数组的内容已被复制,后续对字符数组的修改不会影响新创建的字符
sChar变革的值是字符串“Hello”。切便在创建字符串之后,对a数组中的第2个元素进行了修改,但未影响sChar的值。
4. String(char[]value. int offset, int count)
分配一个新的string,它包含来自 答数组参数一个子数组的字符。offset参数身子数组第一个字符的末引, count参数指定子数组的长度。该子数组的内容已被赋值。 “数组的修改不会影响新创建的字符串。sChar变量的值是字符串"ellio"。该构造方法使用字符数组中的部分连接元素来创建字符串对象。offset参数指定起始末引角, count 指定载现元素的个数,创建字符串对象后,即使在后面修改了a数相中第2个元素的值,对sChar的值也没有任何影响。
二、String和int的相互转换
1、String转换为int
String 字符串转整型 int 有以下两种方式:
1、Integer.parseInt(str)
2、Integer.valueOf(str).intValue()
2、int转换为String
整型int转String字符串类型有以下3种方法:
1. Strings=String,valueOf(i);
2. Strings=Integer.toStringGi.
3. Strings=""+"
使用第三种方法相对第一第二种耗时比较大。在使用第一种walueOf()方法时,注意valueOf括号中的值不能为空,否则会报空指针异常(NullPointerException)。
toString()
toString()可以把一个引用类型转换为Suring字符串类型,是sun公司开发Java的时候为了方便所有类的字符串操作而特意加入的一个方法。
这里chars是存放字符的数组, startindex是字符数组中期望得到的子字符串的首字符下标,numChars指定子字符串的长度。2) parse()
parseXxx(String)这种形式,是指把字符串转换为数值型,其中Xxx对应不同的数据类型,然后转换为Xxx指定的类型,如int型和float型。
3) toString()
toString()可以把一个引用类型转换为Suring字符串类型,是sun公司开发Java的时候为了方便所有类的字符串操作而特意加入的一个方法。
三、字符串拼接(连接)
对于已经定义的字符串,可以对其进行各种操作。连接多个字符串是字符串操作中最简单的一种。通过字符串连接,可以将两个或多个字符串、字符、整数和浮点数等类型的数据连成一个更大的字符串。
String字符串虽然是不可变字符串,但也可以进行拼接只是会产生一个新的对象。String字符串拼接可以使用“+”运算符或String的concat(string str)方法。"+"运算符优势是可以连接任何类型数据拼接成为字符串,而concat方法只能拼接String类型字符串。
1、使用连接运算符“+”
与绝大多数的程序设计语言一样, Java语言允许使用“+”号连接(拼接)两个字符串。“+”远算符是最简单、最快捷,也是使用最多的字符串连接方式。在使用“+”运算符连接字符串和int型(或double型)数据时,“+”将int(或double)型数据自动转换成String类型。
2、使用 concat() 方法
在 Java 中,String 类的 concat() 方法实现了将一个字符串连接到另一个字符串的后面
格式:字符串 1.concat(字符串 2);
3、连接其他类型数据
前面介绍的例子都是字符串与字符串进行连接,其实字符串也可同其他基本数据类型进行连接。如果将字符串同这些数据类型数据进行连接,此时会将这些数据直接转换成字符串。
四、获取字符串长度(length())
1、要获取字符串的长度,可以使用 String 类的 length() 方法,其语法形式如下:
字符串名.length();
五、字符串大小写转换
String 类的 toLowerCase() 方法可以将字符串中的所有字符全部转换成小写,而非字母的字符不受影响。语法格式如下:
字符串名.toLowerCase() // 将字符串中的字母全部转换为小写,非字母不受影响
toUpperCase() 则将字符串中的所有字符全部转换成大写,而非字母的字符不受影响。语
六、去除字符串中的空格(trim())
字符串中存在的首尾空格一般情况下都没有任何意义,如字符串“ Hello ”,但是这些空格会影响到字符串的操作,如连接字符串或比较字符串等,所以应该去掉字符串中的首尾空格,这需要使用 String 类提供的 trim() 方法。
trim() 方法的语法形式如下:
```java
字符串名.trim()
如果不确定要操作的字符串首尾是否有空格,最好在操作之前调用该字符串的 trim() 方法去除首尾空格,然后再对其进行操作。
trim() 只能去掉字符串中前后的半角空格(英文空格),而无法去掉全角空格(中文空格)。可用以下代码将全角空格替换为半角空格再进行操作,其中替换是 String 类的 replace() 方法。
`
七、截取(提取)子字符串(substring())
在 String 中提供了两个截取字符串的方法,一个是从指定位置截取到字符串结尾,另一个是截取指定范围的内容。下面对这两种方法分别进行介绍。
1、substring(int beginIndex) 形式
此方式用于提取从索引位置开始至结尾处的字符串部分。调用时,括号中是需要提取字符串的开始位置,方法的返回值是提取的字符串。例如:
2、substring(int begin,int end) 形式
此方法中的 begin 表示截取的起始索引,截取的字符串中包括起始索引对应的字符;end 表示结束索引,截取的字符串中不包括结束索引对应的字符,如果不指定 end,则表示截取到目标字符串末尾。该方法用于提取位置 begin 和位置 end 位置之间的字符串部分。
这里需要特别注意的是, 对于开始位置 begin, Java 是基于字符串的首字符索引为 0 处理的,但是对于结束位置 end,Java 是基于字符串的首字符索引为 1 来处理的,如下图所示。
注意:substring() 方法是按字符截取,而不是按字节截取
八、分割字符串(spilt())
tring 类的 split() 方法可以按指定的分割符对目标字符串进行分割,分割后的内容存放在字符串数组中。该方法主要有如下两种重载形式:
```java
str.split(String sign)
str.split(String sign,int limit)
其中它们的含义如下:
**str:**为需要分割的目标字符串。
**sign:**为指定的分割符,可以是任意字符串。
**limit:**表示分割后生成的字符串的限制个数,如果不指定,则表示不限制,直到将整个目标字符串完全分割为止。
使用分隔符注意如下:
1)“.”和“|”都是转义字符,必须得加“\\”。
如果用“.”作为分隔的话,必须写成`String.split("\\.")`,这样才能正确的分隔开,不能用`String.split(".")`。
如果用“|”作为分隔的话,必须写成`String.split("\\|")`,这样才能正确的分隔开,不能用`String.split("|")`。
2)如果在一个字符串中有多个分隔符,可以用“|”作为连字符,比如:“acount=? and uu =? or n=?”,把三个都分隔出来,可以 用`String.split("and|or")`。
九、案例:截取新闻标题
在新闻网站中通常以列表的形式显示最新新闻的动态标题。一般情况下,一行显示一条新闻标题,而新闻标题往往比较长,因此需要对它进行截取,将超出部分显示成一个省略号“…”。
十、字符串的替换
因为要截取的是新闻标题中的前 10 个字符,因此,起始位置从 0 开始,结束位置为 10(索引从 1 开始),即使用 substring(0,10) 就完成了新闻标题的截取操作。
1、replace() 方法
replace() 方法用于将目标字符串中的指定字符(串)替换成新的字符(串),
1、replace() 方法
replace() 方法用于将目标字符串中的指定字符(串)替换成新的字符(串),其语法格式如下:
其中,oldChar 表示被替换的字符串;newChar 表示用于替换的字符串。replace() 方法会将字符串中所有 oldChar 替换成 newChar。
2、replaceFirst() 方法
replaceFirst() 方法用于将目标字符串中匹配某正则表达式的第一个子字符串替换成新的字符串
3、replaceAll() 方法
replaceAll() 方法用于将目标字符串中匹配某正则表达式的所有子字符串替换成新的字符串
十一、字符串比较
字符串比较是常见的操作,包括比较相等、比较大小、比较前缀和后缀串等。在 Java 中,比较字符串的常用方法有 3 个:equals() 方法、equalsIgnoreCase() 方法、 compareTo() 方法。
## 1、equals() 方法
equals() 方法将逐个地比较两个字符串的每个字符是否相同。如果两个字符串具有相同的字符和长度,它返回 true,否则返回 false。对于字符的大小写,也在检查的范围之内。equals() 方法的语法格式如下:
```java
str1.equals(str2);
str1 和 str2 可以是字符串变量, 也可以是字符串字面量。
2、equalsIgnoreCase() 方法
equalsIgnoreCase() 方法的作用和语法与 equals() 方法完全相同,唯一不同的是 equalsIgnoreCase() 比较时不区分大小写。当比较两个字符串时,它会认为 A-Z 和 a-z 是一样的。
3、equals()与==的比较
理解 equals() 方法和`==`运算符执行的是两个不同的操作是重要的。如同刚才解释的那样,equals() 方法比较字符串对象中的字符。而`==`运算符比较两个对象引用看它们是否引用相同的实例。
StringBuffer 类
在 Java 中,除了通过 String 类创建和处理字符串之外,还可以使用 StringBuffer 类来处理字符串。StringBuffer 类可以比 String 类更高效地处理字符串。
因为 StringBuffer 类是可变字符串类,创建 StringBuffer 类的对象后可以随意修改字符串的内容。每个 StringBuffer 类的对象都能够存储指定容量的字符串,如果字符串的长度超过了 StringBuffer 类对象的容量,则该对象的容量会自动扩大。
## 1、创建 StringBuffer 类
StringBuffer 类提供了 3 个构造方法来创建一个字符串,如下所示:
1、StringBuffer() 构造一个空的字符串缓冲区,并且初始化为 16 个字符的容量。
2、StringBuffer(int length) 创建一个空的字符串缓冲区,并且初始化为指定长度 length 的容量。
3、StringBuffer(String str) 创建一个字符串缓冲区,并将其内容初始化为指定的字符串内容 str,字符串缓冲区的初始容量为 16 加上字符串 str 的长度。
## 2、追加字符串
StringBuffer 类的 append() 方法用于向原有 StringBuffer 对象中追加字符串。
转字符串
StringBuffer 类中的 reverse() 方法用于将字符串序列用其反转的形式取代。
删除字符串
StringBuffer 类提供了 deleteCharAt() 和 delete() 两个删除字符串的方法,下面详细介绍。
1、deleteCharAt() 方法
deleteCharAt() 方法用于移除序列中指定位置的字符
2、delete() 方法
delete() 方法用于移除序列中子字符串的字符
其中,start 表示要删除字符的起始索引值(包括索引值所对应的字符),end 表示要删除字符串的结束索引值(不包括索引值所对应的字符)。该方法的作用是删除指定区域以内的所有字符,
String、StringBuffer和StringBuilder区别
在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。String 类是不可变类,即一旦一个 String 对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
Java 提供了两个可变字符串类 StringBuffer 和 StringBuilder,中文翻译为“字符串缓冲区”。
StringBuilder 类是 JDK 1.5 新增的类,它也代表可变字符串对象。实际上,StringBuilder 和 StringBuffer 功能基本相似,方法也差不多。不同的是,StringBuffer 是线程安全的,而 StringBuilder 则没有实现线程安全功能,所以性能略高。因此在通常情况下,如果需要创建一个内容可变的字符串对象,则应该优先考虑使用 StringBuilder 类。
StringBuffer、StringBuilder、String 中都实现了 CharSequence 接口。CharSequence 是一个定义字符串操作的接口,它只包括 length()、charAt(int index)、subSequence(int start, int end) 这几个 API。
StringBuffer、StringBuilder、String 对 CharSequence 接口的实现过程不一样
数字和日期处理
一、Math类的常用方法
Java 中的 +、-、*、/ 和 % 等基本算术运算符不能进行更复杂的数学运算,例如,三角函数、对数运算、指数运算等。于是 Java 提供了 Math 工具类来完成这些复杂的运算。
在 Java 中 Math 类封装了常用的数学运算,提供了基本的数学操作,如指数、对数、平方根和三角函数等。Math 类位于 java.lang 包,它的构造方法是 private 的,因此无法创建 Math 类的对象,并且 Math 类中的所有方法都是类方法,可以直接通过类名来调用它们。
1、静态常量
Math 类中包含 E 和 PI 两个静态常量,正如它们名字所暗示的,它们的值分别等于 e(自然对数)和 π(圆周率)。
生成随机数(random()和Random类)
在 Java 中要生成一个指定范围之内的随机数字有两种方法:一种是调用 Math 类的 random() 方法,一种是使用 Random 类。
Random 类提供了丰富的随机数生成方法,可以产生 boolean、int、long、float、byte 数组以及 double 类型的随机数,这是它与 random() 方法最大的不同之处。random() 方法只能产生 double 类型的 0~1 的随机数。
Random 类位于 java.util 包中,该类常用的有如下两个构造方法。
**Random():**该构造方法使用一个和当前系统时间对应的数字作为种子数,然后使用这个种子数构造 Random 对象。
**Random(long seed):**使用单个 long 类型的参数创建一个新的随机数生成器。
Random 类提供的所有方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的概率是均等的
ava时间日期的处理
Date 类
Date 类表示系统特定的时间戳,可以精确到毫秒。Date 对象表示时间的默认顺序是星期、月、日、小时、分、秒、年。
DateFormat 类
DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)允许进行格式化(也就是日期→文本)、解析(文本→日期)和标准化日期。
内置类及包装类
包装类、装箱和拆箱
包装类
Java 的设计中提倡一种思想,即一切皆对象。但是从数据类型的划分中,我们知道 Java 中的数据类型分为基本数据类型和引用数据类型,但是基本数据类型怎么能够称为对象呢?于是 Java 为每种基本数据类型分别设计了对应的类,称之为包装类(Wrapper Classes),也有地方称为外覆类或数据类型类。
装箱和拆箱
基本数据类型转换为包装类的过程称为装箱,例如把 int 包装成 Integer 类的对象;包装类变为基本数据类型的过程称为拆箱,例如把 Integer 类的对象重新简化为 int。
Object 类
Object 是 Java 类库中的一个特殊类,也是所有类的父类。也就是说,Java 允许把任何类型的对象赋给 Object 类型的变量。当一个类被定义后,如果没有指定继承的父类,那么默认父类就是 Object 类
Integer类
Integer 类在对象中包装了一个基本类型 int 的值。Integer 类对象包含一个 int 类型的字段。此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法。
System类
System 类位于 java.lang 包,代表当前 Java 程序的运行平台,系统级的很多属性和控制方法都放置在该类的内部。由于该类的构造方法是 private 的,所以无法创建该类的对象,也就是无法实例化该类。
Lambda 表达式
一、Java 中 Lambda 表达式
1、Lambda 简介
Lambda 表达式(Lambda expression)是一个匿名函数,基于数学中的λ演算得名,也可称为闭包(Closure)。现在很多语言都支持 Lambda 表达式,如 C++、C#、Java、 Python 和 JavaScript 等。
Lambda 表达式是推动 Java 8 发布的重要新特性,它允许把函数作为一个方法的参数(函数作为参数传递进方法中)
Calculable 接口只有一个方法 calculateInt,参数是两个 int 类型,返回值也是 int 类型。
Java 中常见的输出函数:
1、printf 主要继承了C语言中 printf 的一些特性,可以进行格式化输出。
2、print 就是一般的标准输出,但是不换行。
3、println 和 print 基本没什么差别,就是最后会换行。
2、Java Lambda 表达式的优缺点
优点:
代码简洁,开发迅速
方便函数式编程
非常容易进行并行计算
Java 引入 Lambda,改善了集合操作(引入 Stream API)
缺点:
代码可读性变差
在非并行计算中,很多计算未必有传统的 for 性能要高
不容易进行调试
3、函数式接口
Lambda 表达式实现的接口不是普通的接口,而是函数式接口。如果一个接口中,有且只有一个抽象的方法(Object 类中的方法不包括在内),那这个接口就可以被看做是函数式接口。
提示:Lambda 表达式是一个匿名方法代码,Java 中的方法必须声明在类或接口中,那么 Lambda 表达式所实现的匿名方法是在函数式接口中声明的。
二、Lambda表达式的使用
1、作为参数使用Lambda表达式
Lambda 表达式一种常见的用途就是作为参数传递给方法,这需要声明参数的类型声明为函数式接口类型。
2、访问变量
Lambda 表达式可以访问所在外层作用域定义的变量,包括成员变量和局部变量。
2.1、访问成员变量
成员变量包括实例成员变量和静态成员变量。在 Lambda 表达式中可以访问这些成员变量,此时的 Lambda 表达式与普通方法一样,可以读取成员变量,也可以修改成员变量。
2.2、访问局部变量
对于成员变量的访问 Lambda 表达式与普通方法没有区别,但是访问局部变量时,变量必须是 final 类型的(不可改变)。
Lambda 表达式只能访问局部变量而不能修改,否则会发生编译错误,但对静态变量和成员变量可读可写。
3、方法引用
方法引用可以理解为 Lambda 表达式的快捷写法,它比 Lambda 表达式更加的简洁,可读性更高,有很好的重用性。如果实现比较简单,复用的地方又不多,推荐使用 Lambda 表达式,否则应该使用方法引用。
注意:被引用方法的参数列表和返回值类型,必须与函数式接口方法参数列表和方法返回值类型一致
异常处理
计算机程序的编写也需要考虑处理这些异常情况。异常(exception)是在运行程序时产生的一种异常情况,已经成为了衡量一门语言是否成熟的标准之一。目前的主流编程语言,如 C++、c#、Ruby 和 Python 等大都提供了异常处理机制。
Java 中的异常又称为例外,是一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流。为了能够及时有效地处理程序中的运行错误,必须使用异常类,这可以让程序具有极好的容错性且更加健壮。
在 Java 中一个异常的产生,主要有如下三种原因:
1、Java 内部错误发生异常,Java 虚拟机产生的异常。
2、编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。
3、通过 throw 语句手动生成的异常,一般用来告知该方法的调用者一些必要信息。
Java 通过面向对象的方法来处理异常。在一个方法的运行过程中,如果发生了异常,则这个方法会产生代表该异常的一个对象,并把它交给运行时的系统,运行时系统寻找相应的代码来处理这一异常。
我们把生成异常对象,并把它提交给运行时系统的过程称为拋出(throw)异常。运行时系统在方法的调用栈中查找,直到找到能够处理该类型异常的对象,这一个过程称为捕获(catch)异常。
为了能够及时有效地处理程序中的运行错误,Java 专门引入了异常类。
Error(错误)和 Exception(异常)都是 java.lang.Throwable 类的子类,在 Java 代码中只有继承了 Throwable 类的实例才能被 throw 或者 catch。
Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类,Exception 是程序正常运行过程中可以预料到的意外情况,并且应该被开发者捕获,进行相应的处理。Error 是指正常情况下不大可能出现的情况,绝大部分的 Error 都会导致程序处于非正常、不可恢复状态。所以不需要被开发者捕获。
Error 错误是任何处理技术都无法恢复的情况,肯定会导致程序非正常终止。并且 Error 错误属于未检查类型,大多数发生在运行时。Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源码里必须显示的进行捕获处理,这里是编译期检查的一部分。不检查异常就是所谓的运行时异常,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译器强制要求。
如下是常见的 Error 和 Exception:
1)运行时异常(RuntimeException): NullPropagation:空指针异常; ClassCastException:类型强制转换异常 IllegalArgumentException:传递非法参数异常 IndexOutOfBoundsException:下标越界异常 NumberFormatException:数字格式异常
2)非运行时异常: ClassNotFoundException:找不到指定 class 的异常 IOException:IO 操作异常
3)错误(Error): NoClassDefFoundError:找不到 class 定义异常 StackOverflowError:深递归导致栈被耗尽而抛出的异常 OutOfMemoryError:内存溢出异常
Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。
Java 的异常处理机制提供了一种结构性和控制性的方式来处理程序执行期间发生的事件。异常处理的机制如下: 1、在方法中用 try catch 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个异常。 2、对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 语句拋出异常,即由上层的调用方法来处理。
- try catch
Java 的异常处理通过 5 个关键字来实现:try、catch、throw、throws 和 finally。try catch 语句用于捕获并处理异常,finally 语句用于在任何情况下(除特殊情况外)都必须执行的代码,throw 语句用于拋出异常,throws 语句用于声明可能会出现的异常。
在 Java 中通常采用 try catch 语句来捕获异常并处理。
注意:try...catch 与 if...else 不一样,try 后面的花括号{ }不可以省略,即使 try 块里只有一行代码,也不可省略这个花括号。与之类似的是,catch 块后的花括号{ }也不可以省略。另外,try 块里声明的变量只是代码块内的局部变量,它只在 try 块内有效,其它地方不能访问该变量。
多重catch语句
如果 try 代码块中有很多语句会发生异常,而且发生的异常种类又很多。那么可以在 try 后面跟有多个 catch 代码块。
在多个 catch 代码块的情况下,当一个 catch 代码块捕获到一个异常时,其它的 catch 代码块就不再进行匹配。
注意:当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。
try catch finally语句
在实际开发中,根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。
使用 try-catch-finally 语句时需注意以下几点:
1、异常处理语法结构中只有 try 块是必需的,也就是说,如果没有 try 块,则不能有后面的 catch 块和 finally 块;
2、catch 块和 finally 块都是可选的,但 catch 块和 finally 块至少出现其中之一,也可以同时出现;
3、可以有多个 catch 块,捕获父类异常的 catch 块必须位于捕获子类异常的后面;
4、不能只有 try 块,既没有 catch 块,也没有 finally 块;
5、多个 catch 块必须位于 try 块之后,finally 块必须位于所有的 catch 块之后。
6、finally 与 try 语句块匹配的语法格式,此种情况会导致异常丢失,所以不常见。
一般情况下,无论是否有异常拋出,都会执行 finally 语句块中的语句
throws 声明异常
当一个方法产生一个它不处理的异常时,那么就需要在该方法的头部声明这个异常,以便将该异常传递到方法的外部进行处理。使用 throws 声明的方法表示此方法不处理异常。
使用 throws 声明抛出异常的思路是,当前方法不知道如何处理这种类型的异常,该异常应该由向上一级的调用者处理;如果 main 方法也不知道如何处理这种类型的异常,也可以使用 throws 声明抛出异常,该异常将交给 JVM 处理。JVM 对异常的处理方法是,打印异常的跟踪栈信息,并中止程序运行,这就是前面程序在遇到异常后自动结束的原因。
方法重写时声明抛出异常的限制
使用 throws 声明抛出异常时有一个限制,是方法重写中的一条规则:子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同,子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。
throw 拋出异常
与 throws 不同的是,throw 语句用来直接拋出一个异常,后接一个可拋出的异常类对象
其中,ExceptionObject 必须是 Throwable 类或其子类的对象。如果是自定义异常类,也必须是 Throwable 的直接或间接子类
当 throw 语句执行时,它后面的语句将不执行,此时程序转向调用者程序,寻找与之相匹配的 catch 语句,执行相应的异常处理程序。如果没有找到相匹配的 catch 语句,则再转向上一层的调用程序。这样逐层向上,直到最外层的异常处理程序终止程序并打印出调用栈情况。
throws 关键字和 throw 关键字在使用上的几点区别如下: 1、throws 用来声明一个方法可能抛出的所有异常信息,表示出现异常的一种可能性,但并不一定会发生这些异常;throw 则是指拋出的一个具体的异常类型,执行 throw 则一定抛出了某种异常对象。 通常在一个方法(类)的声明处通过 throws 声明方法(类)可能拋出的异常信息,而在方法(类)内部通过 throw 声明一个具体的异常信息。 2、throws 通常不用显示地捕获异常,可由系统自动将所有捕获的异常信息抛给上级方法; throw 则需要用户自己捕获相关的异常,而后再对其进行相关包装,最后将包装后的异常信息抛出。
自定义异常
如果 Java 提供的内置异常类型不能满足程序设计的需求,这时我们可以自己设计 Java 类库或框架,其中包括异常类型。实现自定义异常类需要继承 Exception 类或其子类,如果自定义运行时异常类需继承 RuntimeException 类或其子类。
自定义异常类一般包含两个构造方法:一个是无参的默认构造方法,另一个构造方法以字符串的形式接收一个定制的异常消息,并将该消息传递给超类的构造方法。
异常跟踪栈
异常对象的 printStackTrace() 方法用于打印异常的跟踪栈信息,根据 printStackTrace() 方法的输出结果,开发者可以找到异常的源头,并跟踪到异常一路触发的过程。
异常跟踪栈信息的第一行一般详细显示异常的类型和异常的详细消息,接下来是所有异常的发生点,各行显示被调用方法中执行的停止位置,并标明类、类中的方法名、与故障点对应的文件的行。一行行地往下看,跟踪栈总是最内部的被调用方法逐渐上传,直到最外部业务操作的起点,通常就是程序的入口 main 方法或 Thread 类的 run 方法(多线程的情形)。
提示:虽然 printStackTrace() 方法可以很方便地用于追踪异常的发生情况,可以用它来调试程序,但在最后发布的程序中,应该避免使用它。应该对捕获的异常进行适当的处理,而不是简单地将异常的跟踪栈信息打印出来。
集合
在编程时,可以使用数组来保存多个对象,但数组长度不可变化,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的。如果需要保存数量变化的数据,数组就有点无能为力了。而且数组无法保存具有映射关系的数据,如成绩表为语文——79,数学——80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组),Java 提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。
集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量),而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
Java 集合类型分为 Collection 和 Map,它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。图 1 和图 2 分别为 Collection 和 Map 的子接口及其实现类。
对于 Set、List、Queue 和 Map 这 4 种集合,Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。
Collection接口
Collection 接口是 List、Set 和 Queue 接口的父接口,通常情况下不被直接使用。Collection 接口定义了一些通用的方法,通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合。
注意:以上方法完全来自于 Java API 文档,可自行参考 API 文档来查阅这些方法的详细信息。无需硬性记忆这些方法,可以和实际生活结合记忆。集合类就像容器,现实生活中容器的功能,就是添加对象、删除对象、清空容器和判断容器是否为空等,集合类为这些功能都提供了对应的方法。
List集合
List 是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List 集合默认按元素的添加顺序设置元素的索引,第一个添加到 List 集合中的元素的索引为 0,第二个为 1,依此类推。
List 实现了 Collection 接口,它主要有两个常用的实现类:ArrayList 类和 LinkedList 类。
- ArrayList 类
ArrayList 类实现了可变数组的大小,存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式,对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合,允许对集合中的元素进行快速的随机访问,不过,向 ArrayList 中插入与删除元素的速度相对较慢。
ArrayList 类的常用构造方法有如下两种重载形式:
1、ArrayList():构造一个初始容量为 10 的空列表。
2、ArrayList(Collection<?extends E>c):构造一个包含指定 Collection 元素的列表,这些元素是按照该 Collection 的迭代器返 回它们的顺序排列的。
ArrayList 类除了包含 Collection 接口中的所有方法之外,还包括 List 接口中提供的如下表所示的方法。
注意:当调用 List 的 set(int index, Object element) 方法来改变 List 集合指定索引处的元素时,指定的索引必须是 List 集合的有效索引。例如集合长度为 4,就不能指定替换索引为 4 处的元素,也就是说这个方法不会改变 List 集合的长度。
LinkedList类
LinkedList 类采用链表结构保存对象,这种结构的优点是便于向集合中插入或者删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高,但是 LinkedList 类随机访问元素的速度则相对较慢。这里的随机访问是指检索集合中特定索引位置的元素。
LinkedList<String> 中的 <String> 是 Java 中的泛型,用于指定集合中元素的数据类型,例如这里指定元素类型为 String,则该集合中不能添加非 String 类型的元素。
ArrayList 类和 LinkedList 类的区别
ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的所有未实现的方法,只是实现的方式有所不同。
ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
Set集合
Set 集合类似于一个罐子,程序可以依次把多个对象“丢进”Set 集合,而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合。Set 集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。
Set 实现了 Collection 接口,它主要有两个常用的实现类:HashSet 类和 TreeSet类
HashSet 类
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。
HashSet 具有以下特点: 1、不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。 2、HashSet 不是同步的,如果多个线程同时访问或修改一个 HashSet,则必须通过代码来保证其同步。 3、集合元素值可以是 null。
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据该hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals() 方法比较返回的结果为 true,但它们的 hashCode 不相等,HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说,两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。
在 HashSet 类中实现了 Collection 接口中的所有方法。HashSet 类的常用构造方法重载形式如下。 1、HashSet():构造一个新的空的 Set 集合。 2、HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示 HashSet 的父类,即指明该 Set 集合中存放的集合元素类型。c 表示其中的元素将被存放在此 Set 集合中。
TreeSet 类
TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序,因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的,这里的自然排序指的是升序排序。
TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo(Object o) 方法用于比较两个对象的大小。例如 a.compareTo(b),如果 a 和 b 相等,则该方法返回 0;如果 a 大于 b,则该方法返回大于 0 的值;如果 a 小于 b,则该方法返回小于 0 的值。
Map集合
Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键(key)对象和一个值(value)对象。用于保存具有映射关系的数据。
Map 集合里保存着两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 都可以是任何引用类型的数据。Map 的 key 不允许重复,value 可以重复,即同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。
Map 中的 key 和 value 之间存在单向一对一关系,即通过指定的 key,总能找到唯一的、确定的 value。从 Map 中取出数据时,只要给出指定的 key,就可以取出对应的 value。
Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。
Map 集合最典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该 Map 中是否包含指定 key,也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合,进而遍历 Map 中所有的 key-value 对。