发展:
1.SE 标准版。
2.EE 企业版,基于SE。
3.ME 已经淘汰。
JDK、JRE和JVM:
JDK:
称为Java开发工具包( Java Development Kit)。Java开发人士需要下载和安装JDK,目前的主流版本为JDK11。
JRE:
称之为Java SE运行时环境(Java SE Runtime Environment),提供了运行Java应用程序所必须的软件环境等。无论是开发还是运行Java应用都必须安装。
区别:
JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。
JRE包含JVM和类库
JVM多个系统上都有实现,使得跨平台能够实现。
javac和java:
javac.exe编译器,主要用于将高级Java源代码翻译成字节码文件。生成对应的xxx.class,即可通过java xxx执行
java.exe解释器,主要用于启动JVM对字节码文件进行解释并执行。新特性:java xxx.java可以直接在版本11运行,如果当前路径存在编译后的class,会报错。
常见操作:
1.注释
单行用//,多行用/* */,文档注释/** */可以提取
2.规范
xxx.java开头用注释声明项目名称、功能、作者、版本、备注
3.xxx.java里面要有这个类xxx
public class xxx {
public static void main(String[] args){
}
}
4.变量的声明方式
数据类型 变量名 = 初始值; //其中=初始值可以省略,但;不可以省略。
变量使用之前需要声明和初始化。
不能重复声明
5.标识符的命名法则
• 由数字、字母、下划线以及$等组成,其中数字不能开头(不能区分字面值)。
• 不能使用Java语言的关键字,所谓关键字就是Java语言用于表示特殊含义
的单词。
• 区分大小写,长度没有限制但不宜过长。
• 尽量做到见名知意,支持中文但不推荐使用。
• 标识符可以给类/变量/属性/方法/包 起名字。
6.变量的输入
Scanner scanner = new Scanner(System.in);
api的使用可以参考手册index.html
基本数据类型:
概述:
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
1. byte(Byte)
byte 数据类型是8位、有符号的,以二进制补码表示的整数;
最小值是 -128(-2^7),最大值是 127(2^7-1,最高位为0表示非负),默认值是 0;用在大型数组中节约空间。
2. short(Short)
short 数据类型是 16 位、有符号的以二进制补码表示的整数
最小值是 -32768(-2^15);最大值是 32767(2^15 - 1);默认值是 0;
3. int(Integer)
int 数据类型是32位、有符号的以二进制补码表示的整数;
最小值是 -2,147,483,648(-2^31);最大值是 2,147,483,647(2^31 - 1);一般地整型变量默认为 int 类型;默认值是 0 ;
正无穷为∞(字节'\u221e'),负无穷为-∞。print输出为Infinity。
4. long(Long)
long 数据类型是 64 位、有符号的以二进制补码表示的整数
默认值是 0L;long a = 100000L,"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
默认的数字字面值解释器声明为int类型,然后转换为定义的byte、short,int。
但int a=25;byte b=a;解释器会报错,因为a是变量,存储的值还不固定,和数字字面值不同,所以解释器直接报错了。
如果超过int范围,会报错整数太大。需要显式加上l或L。
更大的数可以用java.math.BigInteger
BigInteger a = new BigInteger("25025050060000000000000000");
5. float(Float)
float 数据类型是单精度、4个字节32位、符合IEEE 754标准的浮点数,可以表示7位有效数字;范围:-3.403E38~3.403E38。(因为有些位数用来表示指数,所以范围更大了)
float 在储存大型浮点数组的时候可节省内存空间;默认值是 0.0f;浮点数不能用来表示精确的值,如货币;
例子:float f1 = 234.5f/F(不加f的话double转float,报错)。会自动忽略超过位数。
6. double(Double)(推荐,有效位数更多)
double 数据类型是双精度、8个字节64 位、符合IEEE 754标准的浮点数,可以表示15位有效数字;范围:-1.798E308~1.798E308。
浮点数的默认类型为double类型;double类型同样不能表示精确的值,如货币;默认值是 0.0d;运算不精确。比如0.1+0.2=0.3000004。
精确可以用java.math.BigDecimal。System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
例子:double d1 = 123.4。
7. boolean
boolean数据类型表示一位的信息;只有两个取值:true 和 false;默认值是 false;内存空间中所占大小没有明确的规定,可以认为是1个字节。
8. char(Character)
char类型是一个单一的 2个字节 16 位(没有符号位) Unicode 字符;
单引号。
最小值是 \u0000(即为0);最大值是 \uffff(即为65,535,UCS-2统一码版本,其中汉字一共2w多个,基本满足各种语言的使用,最新版本UCS-4是31位字符集);char 数据类型可以储存任何字符;
扩展:有两个实现utf-8采用变长字节,1-4位字节变化,由于额外的标志信息,4个字节一共64029个(3个字节一共52156个,汉字常用);utf-16两个字节
例子:
char letter = 'A'。
char d=1会自动转换为ascii码对应的字符,计算机的底层只识别0和1组成的二进制序列。(int)d即可打印对应的数字。
引用数据类型:
数组、类、接口、枚举、标注
进制转换:
10转2:
二进制(ob/oB开头)中的最高位(最左边)用于代表符号位,若该位是0则表示非负数, 若该位是1则表示负数。
1.除2取余法,使用十进制整数不断地除以2取出余数,直到商为0时将余数逆序排序。
2.拆分法,将十进制整数拆分为若干个二进制权重(1,2,4,8)的和,有该权重下面 写1,否则写0。
2转10:
1.加权法,使用二进制中的每个数字乘以当前位的权重再累加起来。
负十进制转换为二进制的方式:
先将十进制的绝对值转换为二进制,然后进行按位取反再加1。(涉及到补码的概念,正负相加为0,溢出丢弃)
负二进制转换为十进制的方式:
先减1再按位取反,合并为十进制整数后添加负号。
16进制:
以0x开头
8进制:
以0开头
字节与bit:
公式:
1B(byte,字节)= 8 bit(位)
为什么?
一个字节定义为可容纳单个字符的最小bits
它并不总是8,有时是7或9。这取决于平台。
一个说法是使用8个字符(方便的2乘幂),有时他们将第8位用于奇偶校验,有时又将其用于ASCII标准的扩展。有时他们只是将第8位设置为零。
26个英文字母大小写52个, 加上10个数字,还有一些特殊符号超过了64个,所以ascll128个, 共计需要连续七组信号值, 但是早期的传输不可靠, 于是预留了一位作为奇偶校验, 共八组信号值, 一字节8位
类型转换:
自动类型转换:
从小类型到大类型之间的转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
byte,short,char—> int —> long—> float —> double
强制类型转换:
概述:
从大类型到小类型之间的转换
格式:
变量名 = (目标类型)源类型变量名;
数据类型转换必须满足如下规则:
// 子类也可以通过这种强制类型转换 变为父类
1. 不能对boolean类型进行类型转换。
2. 不能把对象类型转换成不相关类的对象。
3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
4. 转换过程中可能导致溢出或损失精度,例如:
int i =128;
byte b = (byte)i;
因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出。
5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;
(int)-45.89f == -45
运算符:
算术运算符:
+ 表示加法运算符,同时可以实现字符串(至少一边即可)与其他数据类型“相连”。(表明java为弱类型语言)
- 表示减法运算符
* 表示乘法运算符
/ 表示除法运算符,只保留整数部分,要想保留小数需要其中一个为double,或者乘以0.1。不能除以0,除以0.0得到infinity,0/0得到NaN
% 表示取模/取余运算符
自增减运算符:
只能用于变量,常数不可以
a++:是一个表达式,先让a的数值作为整个表达式的最终结果,然后再让a变量加1
++:a让a变量加1,再让a的数值作为整个表达式的最终结果。少了一步声明赋值,效率更高。
a--
--a
逻辑运算符:
&& 表示逻辑与运算符,相当于"并且",同真为真,一假为假。
|| 表示逻辑或运算符,相当于"或者",一真为真,同假为假。
! 表示逻辑非运算符,相当于"取反",真为假,假为真。
逻辑运算符的操作数均为boolean表达式。
短路特性
对于逻辑与运算符来说,若第一个表达式为假则结果为假,此时跳过第 二个表达式;
对于逻辑或运算符来说,若第一个表达式为真则结果为真,此时跳过第 二个表达式;
条件/三目运算符:
条件表达式? 表达式1: 表达式2
判断条件表达式是否成立,若成立则执行表达式1,否则执行表达式2 。
赋值运算符:
= 表示赋值运算符,用于将=右边的数据赋值给=左边的变量,覆盖变量 原来的数值。
赋值表达式本身也有值,其本身之值即为所赋之值。
+=、 -=、 *=、 /=、 ...
注意:
byte a=10;
a = a+10;会报错,编译器做了优化,将a和10都变为int类型然后相加,结果为int类型。需要强转换。
而a+=10等价于a=(byte)(a+10),所以没有报错。
规范:
a==2
2==a两者的区别是少写一个等号的时候,能在编译阶段报错。
移位运算符:
<< 左移运算符,用于将数据的二进制位向左移动,右边使用0补充
左移1位通常结果*2,在没超出范围的情况下。
>> 右移运算符,用于将数据的二进制位向右移动,左边使用符号位补充
右移1位相当于除2
>>> 表示逻辑右移运算符,用于将数据的二进制位向右移动,左边使用0 补充。
非负时和右移一样
位运算符:
& 表示按位与运算符,按照二进制位进行与运算,同1为1,一0为0.
| 表示按位或运算符,按照二进制位进行或运算,一1为1,同0为0.
~ 表示按位取反运算符,按照二进制位进行取反,1为0,0为1.
^ 表示按位异或运算符,按照二进制位进行异或运算,同为0,不同为1.
优先级:
[]()(方法调用) 从左到右
!~++--+(一元运算)-(一元运算) 从右到左
*/% 从左到右
+- 从左到右
<< >> >>> 从左到右
== != 从左到右
& 从左到右
^ 从左到右
| 从左到右
&& 从左到右
|| 从左到右
?: 从右到左
= 从右到左
流程控制:
if语句:
if(){}
if else
if else if else
switch case分支结构:
从上到下执行
switch(变量/表达式) {
case 字面值1: 语句块1; break;
case 字面值2: 语句块2; break; ...
default:语句块n;
}
switch()中支持的数据类型有:byte、short、char以及int类型,从jdk1.5 开始支持枚举类型,从jdk1.7开始支持String类型。
case穿透:如果执行了一个case没有break,会执行下一行直到break;
default和位置顺序无关,总是在匹配不到的时候才执行。
for循环:
for(初始化表达式; 条件表达式; 修改初始值表达式) { 循环体;
}
for(;;) - 这种没有循环条件的循环叫做 无限循环,俗称“死循环”。
如果要退出外层循环体,需要使用标号的方式。
outer: for (...) {
for(...) {
break outer;
}
}
while循环:
while(条件表达式) {
循环体;
}
注意:
while(条件表达式);{}相当于
while(条件表达式){:}{},这会导致i不会增加, 空语句,用于延时。
do while循环(熟悉):
do {
循环体;
} while(条件表达式); //这里有个分号
数组:
概述:
Java 语言中提供的数组是用来存储固定大小的同类型元素。
声明:
首先必须声明数组变量,才能在程序中使用数组。
dataType[] arrayRefVar; // 首选的方法
dataType arrayRefVar[]; // 效果相同,但不是首选方法,dataType arrayRefVar[] 风格是来自 C/C++ 语言 ,在Java中采用是为了让 C/C++ 程序员能够快速理解java语言。
创建:
dataType[] arrayRefVar = new dataType[arraySize];
dataType[] arrayRefVar = {value0, value1, ..., valuek};
dataType[] arrayRefVar = new dataType[]{value0, value1, ..., valuek};
示例:
int[] arr = new int[2];默认值填充
索引:
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。
遍历:
for (int i = 1; i < myList.length; i++)
for(type element: array)
内存结构分析:
栈区
栈用于存放程序运行过程当中所有的局部变量。一个运行的Java程序从开 始到结束会有多次变量的声明。
堆区
JVM会在其内存空间中开辟一个称为“堆”的存储空间,这部分空间用于存 储使用new关键字创建的数组和对象。
示例:
int[] a = new int[2];这句话,先在栈区声明一个变量,然后在堆区初始化数组,最后赋值操作是在栈区存储堆区的内存地址(引用类型)
增删改查:
数组赋值arr=brr,会把brr的内存地址赋值给arr,arr的长度元素都变为brr。
拷贝:System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
优缺点:
可以直接通过下标(或索引)的方式访问指定位置的元素,速度很快。
数组要求所有元素的类型相同。
数组要求内存空间连续,并且长度一旦确定就不能修改。
增加和删除元素时可能移动大量元素,效率低。
Arrays 类:
java.util.Arrays 类能方便地操作数组(遍历、查找、排序),它提供的所有方法都是静态的。
方法:
static String toString(int[] a)
public static void fill(int[] a, int val) //将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。
示例:
Arrays.fill(a, 1,3,10); 将1到3不含3的位置的元素填充为10,注意不能超索引
public static void sort(Object[] a) //对指定对象数组根据其元素的自然顺序进行升序排列。
public static boolean equals(long[] a, long[] a2) //如果两个数组以相同顺序包含相同的元素,则两个数组是相等的。返回true
public static int binarySearch(Object[] a, Object key) //用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。
//数组在调用前必须排序好的(二分查找)。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
copyOfRange(li,start,end)切片复制,生成另一个数组
二维数组:
声明和初始化:
数据类型[][] 数组名称 = new 数据类型[行数][列数]; 其中列数可以忽略。表示不定长。
数据类型[][] 数组名称 = {
{元素1, 元素2,...}, ...};
面向对象编程:
概念:
面向对象指以属性和行为的观点去分析现实生活中的事物。
面向对象编程指先以面向对象的思想进行分析,然后使用面向对象的编程语言进行表达的过程。
面向过程指侧重具体的步骤,面向对象则是针对能实现步骤的对象。
理解面向对象的思想精髓(封装、继承、多态),至少掌握一种编程语言。
类和对象的概念:
类简单来就是“分类”,是对具有相同特征和行为的多个对象共性的抽象描述,在Java语言中体现为一种引用数据类型,里面包含了描述特征/属性的成员变量以及描述行为的成员方法。
类是用于构建对象的模板,对象的数据结构由定义它的类来决定。
类和成员变量的定义:
class 类名{ 通常情况下,当类名由多个单词组成时,要求每个单词首字母都要大写。
类体;
数据类型成员变量名= 初始值; 当成员变量由多个单词组成时,通常要求从第二个单词起每个单词的首字母大写。 初始值一般不写
}
对象的创建:
a.当一个类定义完毕后,可以使用new关键字来创建该类的对象,这个过程叫做类的实例化。
b.创建对象的本质就是在内存空间的堆区申请一块存储区域,用于存放该对象独有特征信息。
成员变量的默认值:
当变量作为作为类成员使用时,java才确保给定其初始值,防止程序运行时错误。
byte short int long float double char 0/'/uoooo'
boolean false
引用类型 null
引用的定义:
基本概念:
a.使用引用数据类型定义的变量叫做引用型变量,简称为"引用"。
b.引用变量主要用于记录对象在堆区中的内存地址信息,便于下次访问。
语法格式:
类名引用变量名;
引用变量名.成员变量名;
方法:
定义:
class 类名{
返回值类型 成员方法名(形参列表) { 当成员方法名由多个单词组成时,要求从第二个单词起每个单词的首字母大写
成员方法体;
}
}
方法内访问成员可以直接访问。
数据类型形参变量名1, 数据类型形参变量名2, ...
调用:
引用变量名.成员方法名(实参列表);
可变长参数:
返回值类型方法名(参数的类型... 参数名) 类型固定,看作数组即可。
方法参数部分指定类型的参数个数是可以改变的,也就是0~n个。
一个方法的形参列表中最多只能声明一个可变长形参,并且需要放到参数列表的末尾。
基于数组的实现的语法糖
参数传递:
值传递。把实参传递给形参,方法内部其实是在使用形参。
基本数据类型的变量作为方法的参数传递时,形参变量数值的改变通常不会影响到实参变量的数值,因为两个变量有各自独立的内存空间;
引用数据类型的变量作为方法的参数传递时,形参变量指向内容的改变会影响到实参变量指向内容的数值,因为两个变量指向同一块内存空间
当引用数据类型的变量作为方法的参数传递时,若形参变量改变指向后再改变指定的内容,则通常不会影响到实参变量指向内容的改变,因为两个变量指向不同的内存空间。
形参和实参不能同名,否则当作形参了。
内存结构:
栈用于存放程序运行过程当中所有的局部变量。一个运行的Java程序从开始到结束会有多次方法的调用。
JVM会为每一个方法的调用在栈中分配一个对应的空间,这个空间称为该方法的栈帧。一个栈帧对应一个正在调用中的方法,栈帧中存储了该方法的参数、局部变量等数据。
当某一个方法调用完成后,其对应的栈帧将被清除。
构造方法:
class 类名{
类名(形参列表) {
构造方法体;
}
}
构造方法名与类名完全相同并且没有返回值类型,连void都不许有。
支持重载。
使用new关键字创建对象时会自动调用构造方法实现成员变量初始化工作。
this可以在构造方法里调用其他构造方法
默认构造方法:
当一个类中没有定义任何构造方法时,编译器会自动添加一个无参空构造构造方法,叫做默认/缺省构造方法,如:Person(){}
若类中出现了构造方法,则编译器不再提供任何形式的构造方法。
方法引用:
方法引用通过方法的名字来指向一个方法,可以使语言的构造更紧凑简洁,减少冗余代码。
使用一对冒号 ::
1. 构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
2. 静态方法引用:它的语法是Class::static_method,实例如下:
cars.forEach( Car::collide );
3. 特定类的任意对象的方法引用:它的语法是Class::method实例如下:
cars.forEach( Car::repair );
4. 特定对象的方法引用:它的语法是instance::method实例如下:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
方法重载:
若方法名称相同,参数列表(类型、个数、顺序)不同,这样的方法之间构成重载关系(Overload)。
和返回类型无关。
实际意义
调用者只需要记住一个方法名就可以调用各种不同的版本,来实现各种不同的功能。
this关键字:
若在构造方法中出现了this关键字,则代表当前正在构造的对象。可以print验证。
若在成员方法中出现了this关键字,则代表当前正在调用的对象。
this关键字本质上就是当前类类型的引用变量。
工作原理:
在构造方法中和成员方法中访问成员变量时,编译器会加上this.的前缀,而this.相当于汉语中"我的",
当不同的对象调用同一个方法时,由于调用方法的对象不同导致this关键字不同,从而this.方式访问的结果也就随之不同。
使用方式:
1.当局部变量名与成员变量名相同时,在方法体中会优先使用局部变量(就近原则),若希望使用成员变量,则需要在成员变量的前面加上this.的前缀,明确要求该变量是成员变量(重中之重)。
2.this关键字除了可以通过this.的方式调用成员变量和成员方法外,还可以作为方法的返回值(重点)。
3.在构造方法的第一行可以使用this()的方式来调用本类中的其它构造方法(了解)。
递归:
使用递归必须有递归的规律以及退出条件。
使用递归必须使得问题简单化而不是复杂化。
若递归影响到程序的执行性能,则使用递推取代之。比如费氏数列
代码拆分:
默认导入同级的class,main方法单独放到xxxTest.java当中。
封装:
私有化成员变量,使用private关键字修饰。
提供公有的get和set方法,并在方法体中进行合理值的判断。
在构造方法中调用set方法进行合理值的判断。
JavaBean
一种Java语言写成的可重用组件,其它Java 类可以通过反射机制发现和操作这些JavaBean 的属性。
JavaBean本质上就是符合以下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
static关键字:
基本概念:
使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,也就是整个类只有一份并被所有对象共享,该成员变量随着类的加载准备就绪,与是否创建对象无关。
static关键字修饰的成员可以使用引用.的方式访问,但推荐类名.的方式。
使用方式:
在非静态成员方法中既能访问非静态的成员又能访问静态的成员。(成员:成员变量+ 成员方法,静态成员被所有对象共享)
在静态成员方法中只能访问静态成员不能访问非静态成员。(成员:成员变量+ 成员方法,因为此时可能还没有创建对象)
静态变量可以通过静态方法修改访问
在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用static关键字修饰。(不能滥用static关键字)
内存结构:
静态变量位于方法区(代码区)。指向可能是堆区(单例模式的对象)。
单例设计模式:
流程:
1.私有化构造方法,使用private关键字修饰。
2.声明本类类型的引用指向本类类型的对象,并使用private static关键字共同修饰。
3.提供公有的get方法负责将对象返回出去,并使用public static关键字共同修饰。
实现方式:
饿汉式和懒汉式(if null==xxx再创建),在以后的开发中推荐饿汉式(避免多线程问题)。
双重检查锁
问题:
并发场景下,初始化Singleton 和 将对象地址写到instance字段 的顺序是不确定的。
在某个线程new Singleton()时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了。
然而该对象可能还没有初始化(初始化赋值的业务逻辑)。此时若另外一个线程来调用getInstance,取到的就是状态不正确的对象。(比如一些变量还没赋值完成。))
双重检查锁 + volatile:
禁止未初始化完成时,就将内存地址赋值给instance字段。
构造块和静态代码块:
构造块:
在类体中直接使用{}括起来的代码块。每创建一个对象都会执行一次构造块。
静态代码块:
使用static关键字修饰的构造块。静态代码块随着类加载时执行一次。先于构造块执行(加载阶段),比如加载数据库的驱动包等。
子类构造时执行顺序:
先加载父类,再加载子类,在执行父类的无参构造方法(会先执行构造块),再执行子类的构造方法
继承:
概念:
当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需要编写自己独有特征和行为的机制,叫做继承。
格式:
在Java语言中使用extends(扩展)关键字来表示继承关系。
public class Worker extends Person{} -表示Worker类继承自Person类
其中Person类叫做超类、父类、基类。
其中Worker类叫做派生类、子类、孩子类。
使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件。
特点:
子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承只是不能直接访问(可以通过public方法来访问)。
无论使用何种方式构造子类的对象时,都会首先自动调用父类的无参构造方法(没有super的情况下),来初始化从父类中继承的成员变量以及自己的单独成员变量(super()方法做的事情)。
相当于在构造方法的第一行增加代码super()的效果。
可以手动加super()则解释器不再加(不是第一行会报错)。
也可以super(实参列表),这样就不再调用父类的无参构造方法
如果子类不定义任何构造方法,如果父类定义有参,需要父类定义无参构造方法以便super调用。
如果子类定义了无参构造方法,也需要父类定义无参构造方法以便super隐式或者显式调用。
如果子类定义了有参构造方法,可以不定义无参,需要父类定义有参或者无参构造方法,以便super隐式或者显式调用。
总结:一般都定义比较好。
Java语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父类,但一个父类可以有多个子类。
方法重写:
从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做方法的重写(Override)
调用父类方法:
super.function()
示例:
@override # 说明重写,若没有重写则编译报错。
public void show(){
super.function()
xxx
}
原则:
要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许返回类型为子类类型。
要求方法的访问权限不能变小,可以相同或者变大。(继承本身是扩展,不能变小)
要求方法不能抛出更大的异常(异常机制)。
静态方法:
不能标注@override,但可以起同名的。
访问控制符:
public : 对所有类可见。使用对象:类、接口、变量、方法
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
default (即默认): 在同一包内可见(不同包内的子类不行),不使用任何修饰符。使用对象:类、接口、变量、方法。
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
package包:
格式:
package 包名;
package 包名1.包名2.包名3...包名n;
规范:
org.apache.commons.lang.StringUtil
其中StringUtils是类名而org.apache.commons.lang是多层包名,其含义如下:
org.apache表示公司或组织的信息(是这个公司(或组织)域名的反写);common 表示项目的名称信息;lang 表示模块的名称信息。
导入:
使用import关键字导入包。
使用import关键字导入一个包的静态成员,从Java5.0开始支持。
final关键字:
final关键字修饰类体现在该类不能被继承。
-主要用于防止滥用继承,如:java.lang.String类等。
final关键字修饰成员方法体现在该方法不能被重写但可以被继承(即只能调用)。
-主要用于防止不经意间造成重写,如:java.text.Dateformat类中format方法等。
final关键字修饰成员变量体现在该变量必须初始化(一开始显式初始化或者延迟在构造块/方法中初始化)且不能改变。
-主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等。
常量:
在以后的开发中很少单独使用final关键字来修饰成员变量
通常使用public static final关键字共同修饰成员变量来表达常量的含义,常量的命名规范要求是所有字母都要大写,不同的单词之间采用下划线连。
public static 修饰的常量作用域是全局的,不需要创建对象就可以访问它
多态:
格式:
父类类型引用变量名= new 子类类型();
如Shape sr= new Rect();
特点:
当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法,不可以直接调用子类独有的方法(编译会报错,需要强转)。
对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)(如果子类没有则调用父类)。
对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本。
引用数据类型之间的转换:
引用数据类型之间的转换方式有两种:自动类型转换和强制类型转换。
自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也叫做向上转型。(多态调用)
强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也叫做向下转型或显式类型转换。
条件:
1.引用数据类型之间的转换必须发生在父子类之间,否则编译报错。
2.如果转换发生在父级和兄弟子类或者孙类这种不是父子关系时,编译阶段不报错,但运行阶段会报ClassCastException。
若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常。
使用:
为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
if(引用变量instanceof数据类型)
判断引用变量指向的对象是否为后面的数据类型
实际意义:
屏蔽不同子类的差异性实现通用的编程带来不同的效果。
多态存在的三个必要条件:
1.继承
2.重写
3.父类引用指向子类对象 //Parent p = new Child();
多态的实现方式:
方式一:重写
方式二:接口
方式三:抽象类和抽象方法
多态的使用场景:
1.通过方法的参数传递形成多态
2.抽象类或接口类的引用指向子类,在方法体中直接使用多态的语法格式
3.通过方法的返回值类型形成多态
虚函数:
虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。
如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
重写:
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。要想调用父类中被重写的方法,则必须使用关键字 super。
示例:
Salary s = new Salary("员工 A", "北京", 3, 3600.00);
Employee e = new Salary("员工 B", "上海", 2, 2400.00);
在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句,
但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法
以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。
Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。
抽象方法:
概述:
主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体。
格式:
访问权限 abstract 返回值类型 方法名(形参列表);
兼容的访问权限:
不能private,私有方法不能继承
不能final,需要修改。
不能static,抽象方法不能提升为类层级
default需要同一包内。
抽象类:
概念:
主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象。
和普通类的区别:
不能具体实例化
声明了抽象方法。
特点:
抽象类中可以有成员变量、构造方法、成员方法(供子类super调用)
抽象类中可以没有抽象方法,也可以有抽象方法(抽象类不能实例化,防止误调用抽象方法);
拥有抽象方法的类必须是抽象类
因此真正意义上的抽象类应该是具有抽象方法(不能实例化的意义所在)并且使用abstract关键字修饰的类。
实际意义:
抽象类的实际意义不在于创建对象而在于被继承。
当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式。
格式:
public abstract class Employee
{
public abstract double computePay();
//其余代码
}
接口:
概述:
一种比抽象类还抽象的类,体现在所有方法都为抽象方法。
定义:
定义类的关键字是class,而定义接口的关键字是interface。
里面只能声明常量,可以忽略public static final
从jdk1.9开始允许接口出现私有方法。
方法可以忽略public abstract
支持的修饰符:
public
private
实际意义:
类可以实现多个接口,达到多继承的效果
格式:
Interface关键字用来声明一个接口。
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
使用:
implements A,B 支持多实现,接口之间可以继承。
然后
A xxx = new yyy();可以调用A接口的方法
B xxx = new yyy();可以调用B接口的方法
标记接口:
最常用的继承接口是没有包含任何方法的接口。
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
public interface EventListener
{}
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
1. 建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。
例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
2. 向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
类和接口之间的关系:
名称 关键字 关系
类和类之间的关系 使用extends关键字表达继承关系 支持单继承
类和接口之间的关系 使用implements关键字表达实现关系 支持多实现
接口和接口之间的关系 使用extends关键字表达继承关系 支持多继承
抽象类和接口的主要区别:
定义抽象类的关键字是abstract class,而定义接口的关键字是interface。
继承抽象类的关键字是extends,而实现接口的关键字是implements。
继承抽象类支持单继承,而实现接口支持多实现。(区别3)
抽象类中可以有构造方法,而接口中不可以有构造方法。(区别1)
抽象类中可以有成员变量,而接口中只可以有常量。(区别2)
抽象类中可以有成员方法,而接口中只可以有抽象方法。
抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(Java8以前的版本)。
从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法(工具类功能),但非抽象方法需要使用default关键字修饰(表示接口的默认功能方法,子类可以选择性重写)。
从Java9开始增加新特性,接口中允许出现私有方法。(一般用于定义一些基础方法,供default和static方法调用)
函数式接口:
一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。
如:
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
内部类:
基本概念:
当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类(Inner),而这个内部类所在的类叫做外部类(Outer)。
# 类中的内容:成员变量、成员方法、构造方法、静态成员、构造块和静态代码块、内部类。
实际作用:
当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个类定义为所服务类中的内部类,这样可以隐藏该类的实现细节并且可以方便的访问外部类的私有成员而不再需要提供公有的get和set方法。
分类:
普通内部类-直接将一个类的定义放在另外一个类的类体中。
静态内部类-使用static关键字修饰的内部类,隶属于类层级。
局部内部类-直接将一个类的定义放在方法体的内部时。
匿名内部类-就是指没有名字的内部类。
普通(成员)内部类:
定义:
属于外部类的一个成员,能直接调用外部类的其他成员
特点:
普通内部类和普通类一样可以定义成员变量、成员方法以及构造方法等。
普通内部类和普通类一样可以使用final(限制内部类的继承)或者abstract关键字修饰。
普通内部类还可以使用private(不能用于实例化)或protected关键字进行修饰。
普通内部类需要使用外部类对象来创建对象。Outer.Inner in = ot.new Inner();
如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使用this关键字。(遵循局部优先原则,this/Outer.this分别访问内部和外部对象)
静态内部类:
格式:
访问修饰符staticclass 内部类的类名
使用方式:
静态内部类不能直接访问外部类的非静态成员,可以直接访问静态成员。
静态内部类可以直接创建对象。Outer.Inner in = new Outer.Inner();
如果静态内部类访问外部类中与本类内同名的成员变量或方法时,需要使用类名.的方式访问。
局部(方法)内部类:
格式:
class 内部类的类名
没有修饰符(类似局部变量)
使用方式:
局部内部类只能在该方法的内部可以使用。
局部内部类可以在方法体内部直接创建对象。
局部内部类不能使用访问控制符和static关键字修饰符。
局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致。
默认的局部变量从java8开始默认理解为final关键字修饰的变量,如果修改了,会报错。
原因是防止局部变量拷贝到内部类中修改了,造成内外不一致。(防止修改)
回调模式:
如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中所实现的方法(接口中定义的)。
使用:
自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递;
使用上述匿名内部类的语法格式得到接口/类类型的引用即可;
匿名内部类:
格式:
接口/父类类型 引用变量名= new 接口/父类类型() {方法的重写};
# lambda表达式更简单()->{}
优点:
省去了为类起名字的烦恼
lambda表达式:
概述:
声明一个函数
一种没有声明的方法,即没有访问修饰符,返回值声明和名称。(但是可以返回,类型推断机制)
用途:
1.简写函数式接口(Functional Interface)
2.声明函数,用于其他高阶函数接收,比如列表foreach,字典map
限制:
不能使用闭包
在lambda表达式中对变量的操作都是基于原变量的副本,不会影响到原变量的值。
只能引用标记了 final 的外层局部变量,否则会误以为外部变量的值能够在lambda表达式中被改变。
更先进的写法:
双冒号操作符
枚举:
概述:
在日常生活中这些事物的取值只有明确的几个固定值,此时描述这些事物的所有值都可以一一列举出来,而这个列举出来的类型就叫做枚举类型。
之前的实现方法:
在class中定义多个常量,给外部访问。
缺点:
重复声明比较多
定义:
•使用public static final表示的常量描述较为繁琐,使用enum关键字来定义枚举类型取代常量,枚举类型是从Java5开始增加的一种引用数据类型。
•枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final关键字共同修饰,因此采用枚举类型.的方式调用。
•枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默认也是私有的。
示例:
public enum DirectionEnum {
UP("up"),DOWN("down");
private final String desc;
private DirectionEnum(String desc) {
this.desc = desc;
}
}
switch使用:
function(Enum de){
switch(de){
case UP:
case DOWN:
}
}
更简洁,对输入类型更限制
Enum类:
概念:
所有的枚举类都继承自java.lang.Enum类
方法:
static T[] values()返回当前枚举类中的所有对象
String toString()返回当前枚举类对象的名称
intordinal()获取枚举对象在枚举类中的索引位置
static T valueOf(String str)将参数指定的字符串名转为当前枚举类的对象,要求字符串必须对应存在
intcompareTo (E o)比较两个枚举对象在定义时的顺序,结果为1,0,-1,-2等,当调用对象在参数对象之后时,获取到的比较结果为正数。
实现接口:
不能继承,可以实现。
枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写一个,或者每个对象都重写。
每个对象都重写:
UP("up"){重写方法}
本质上是匿名内部类
注解:
基本概念:
注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型。
注解本质上就是代码中的特殊标记,通过这些标记可以在编译、类加载、以及运行时执行指定的处理。
语法格式:
访问修饰符@interface 注解名称{
注解成员;
}
自定义注解自动继承java.lang.annotation.Annotation接口。
若一个注解中没有任何的成员,则这样的注解叫做标注注解/标识注解。
注解体中只有成员变量没有成员方法,而注解的成员变量以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
如果注解只有一个参数成员,建议使用参数名为value,而类型只能是八种基本数据类型、String类型、Class类型、enum类型及