数组与面向对象

Java类型:基本类型:8个
引用类型:数组、类、接口、枚举

数组:可以一次定义,就可以得到多个类型相同,功能相似的变量。可以通过数组元素的索引来访问数组元素,包括为数组元素赋值和取出数组元素的值。
数组内的元素具有相同的数据类型或有继承关系的数据类型,一个数组只能存储一种数据类型的数据,而不能存储多种数据类型的数据。
一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度是不可改变的,即使数组元素的数据清空,它所占的空间依然被保留,长度依然不变。

数组,在所有数据结构中是存、取数据最快的,数组变量:保存了数组对象的首地址
第i个元素地址=首地址 + i * 元素的大小(元素占字节的位数,如int,占4个字节)
当程序需要取第i个元素时,直接根据计算出来的地址去取值。

数组采用连续的内存地址保存数组中的每一个元素,存放的元素地址是连续的,因此数组可以通过索引快速访问元素。

任何已有的类型,添加一组方括号[]就变成数组类型,如:
int -----> int[] long------> long[] String --------> String[] double------> doule[]
数组其实就是一种引用类型。定义数组变量时,仅仅定义了一个引用变量(也就是一个指针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。因为没有指向任何有效内存空间,因此不能存储元素,不能使用,只有初始化后才可以使用

给数组赋值:初始化:为数组的元素分配内存空间,并为每个数组元素赋初始值。
创建数组对象(数组初始化)
A:静态初始化:显式指定每个数组元素的初始值,由系统决定数组长度
类型[] 变量名 = New 类型[]{元素1,元素2,元素3}
数组元素的类型:是数组类型去掉一组方括号,如: 数组类型int[] ----->元素类型int
数组类型Int[][]----->元素类型int[] 数组类型long[][][]------->元素类型long[][]
静态初始化简化语法: int[] iArr = {3,4,5,56} 这个语法必须在定义数组变量时指定初始值。
B:动态初始化
类型[] 变量名 =new 类型[长度]
只指定长度,不指定元素的值。
系统会自动为每个元素分配初始值,其分配规则是:
如果数组类型为整数类型,数组元素的初始值为0
如果数组类型位浮点类型,数组元素的初始值为0.0
数组元素为boolean类型时,数组元素的初始值为false
如果数组元素为引用类型,数组元素的初始值是null
如果元素的类型是字符类型char,数组元素的初始值是’\u0000’
使用数组:

数组都有一个length属性,该属性返回数组的长度,数组一旦创建,其长度是固定的(它在内存中的位置也是固定的)。
每个数组元素就相当于一个变量,数组元素的类型就是数组类型去掉一组方括号。
访问数组元素的语法:数组变量[索引] ------索引从0开始。
使用的元素超出数组长度,就会导致ArrayIndexoutofBoundsException(数组索引越界异常)
最后一个元素的索引是length -1

遍历数组 - 依次使用每个元素:使用foreach循环遍历数组:
语法:for(元素类型 变量名:数组名)
{
//可通过变量名访问每个元素
}
优点:数组有几个元素,该循环就执行几次,变量会自动、依次等于每个元素。无需迭代语句、循环条件等。
Foreach遍历只能访问数组元素的值,并不能修改数组元素的值。在循环体修改没有意义。循环结束后再次访问数组,会发现在foreach中修改的元素值根本没有改变。
建议:永远不要在foreach循环体修改数组元素的值。

深入数组:数组是引用类型
Java程序必须要使用java虚拟机才能运行(即使用java命令运行的时候会启动虚拟机)
疯狂Java96-109页,图片太多,不做解析
Java程序的内存可分为:
堆(heap)内存:java虚拟器启动时,分配的一块永久的、很大的内存区,运行程序只用一个java虚拟机运行,所以堆内存只有一块。
栈(stack)内存:每次方法运行分配一块临时、很小的内存区。每个方法都有自己对应的栈区(内存),运行几个方法就有几个栈内存,方法结束时,对用的栈区就会被回收。
在方法中定义的局部变量(不管是什么类型)、都放入对应的方法栈。
New出来的对象,都放在堆内存。堆内存中对象,如果没有引用变量指向它,那它就变成了垃圾,等待JVM的垃圾回收器来回收它。
方法中定义的引用变量放在栈内存中,引用变量所指向的对象在堆内存中。
计算机的每个内存单元byte(字节)在操作系统都有一个编号,就像一栋大楼要为每个房间编号一样。
32位操作系统 只能支持2的32次方编号,也就是最大4GB(2的30次方=1G)
变量赋值的区别:
基本类型的赋值:直接将该值存入变量所在内存(变量和值都在栈内存)
引用类型的赋值:将该对象所在第一个内存单元的编号(内存地址)存入变量,引用变量
int it = 20;//4个字节
int[] iArr = new int[4];//数组对象16个字节(4个int)长度4
iArr = new int[]{20,10};//长度2 //再次对引用变量赋值,让iArr重新指向一个新的数组。

int a = 20;
int b = a;//基本类型赋值:直接将值存入变量所在的内存
System.out.println(“a:” + a);//20
System.out.println(“b:” + b);//20
b = 13;//将13存入b所在的内存
System.out.println(“a:” + a);//20
System.out.println(“b:” + b);//13

int[] arr = new int[]{20};//引用类型赋值:将对象所在内存第一个内存单元的编号(首地址)存入变量内存。
int[] brr = arr;//引用类型变量只是存入首地址,相当于arr和brr都指向了同一块堆内存。
System.out.println(“arr[0]:” + arr[0]);//20
System.out.println(“brr[0]:” + brr[0]);//20
brr[0] = 13;//
System.out.println(“arr[0]:” + arr[0]);//13
System.out.println(“brr[0]:” + brr[0]);//13

没有二维数组:其实只有一维数组,二维数组是一维数组的数组
//动态初始化:元素类型是int[],所有数组元素为引用类型,初始值为null。
int[][] arr = new int[5][];
//foreach遍历,元素类型为int[]
for (int[] it : arr)
{
System.out.println(it);
}
//对数组元素赋值,arr[0]是数组元素,是int[]类型,20是int,
//arr[0] = 20报错,不兼容的类型
arr[0] = new int[]{3,5};//静态

生成随机数:
1、使用Random类的nextXxx()方法,其中Xxx表示生成什么类型的数据,next表示生成字符串。
Random r = new Random();
r.nextInt(10)//表示生成0~10之间的整数。
2、使用Math类的random()方法
Math.random()会生成0.0~1.0之间的double数,如果要生成整数,需要转换成int类型
Math.random()*10
获取键盘输入的数:
Scanner

课后练习:
定义长度为10的char[]数组,随机生成10个大写字符。存入其中。并且遍历他们。
定义长度为10的char[]数组,随机生成10个不重复的大写字符。存入其中。并且遍历他们。
生成二维数组,并打印如下:
A B C D E A B C D A B C
F G H I J E F G H D E F
K L M N O H I J K G H I
P Q R S T L M N O
U V W X Y P Q R S

操作数组的工具类:Arrays
主要方法:
toString(type[] a)将数组转换成一个字符串。
parallelSort(xxx[] a)方法和sort()方法相似,都是将数组元素进行排序,只是该方法增加了并行能力,可以利用多CPU并行来提高性能
parallelPrefix(xxx[] a,XxxBinaryOperator op):根据op对象两个参数指定的计算公式计算得到的结果作为新的数组元素,新数组的第一个元素无需计算,直接等于a数组的第一个元素。

面向对象:java是一门面向对象的语言。
类:某一类对象的统称。相当于是一个概念性的(不是具体存在的东西)
对象:现实生活中所能接触的各种“东西”,也称为实例。
三句总诀:定义类、创建对象、调用方法。
定义类
[修饰符] class 类名
{
//成员变量(field)
//方法(method)
//构造器(constructor)
//内部类(nested class)
//初始化块
}-----类体
类中的5大成员。只能有这五大成员,其他的任何都会报错。如:
Double de = 3.4;//不会报错,是定义成员变量。
double de;
de = 3.4;//赋值语句,不是定义成员变量,报错。
类修饰符:public 、final | abstract 有且仅有,用|分割表示是互斥的,只能有其中之一。
类名:语法要求(只要是标识符即可),从专业角度性:多个单词连接而成,每个单词首字母必须大写
成员变量(field):
语法: [修饰符] 类型 变量名 [ = 初始值];
成员变量修饰符:private | protected |public、final、static、(transient:序列化相关)。
类型:任意基本类型或引用类型。
变量名:驼峰写法,第一个单词小写,后面每个单词首字母大写,其余字母小写。
成员变量:是用于描述这个类或对象的状态的。因此,通常建议用名词(如定义一个人类,体重、身高都是状态)项目中,只定义项目感兴趣的状态。

方法(method):
语法: [修饰符] 返回值类型 方法名(形参列表)
{
//代码:定义变量(包括数组)、变量赋值、流程控制、数组
如果声明了返回值类型,必须有return语句。
}-----方法体
修饰符:private|protected|public 、abstract |final、static
返回值类型:任意基本类型或引用类型,可使用void声明没有返回值。
方法名:语法要求:驼峰写法,第一个单词小写,后面每个单词首字母大写,其余字母小写。
方法:用于描述该类或对象的行为,因此通常建议用动词,项目中,只定义感兴趣的行为。
形参列表:(形参类型1 形参名1,形参类型2 形参名2)
每个形参都满足”形参类型 形参名”的格式,多个形参之间用逗号隔开。
形参代表调用方法时要传入的参数。

构造器(constructor):
作用:new调用构造器来创建对象。
如果没有为类写构造器,系统会默认为类提供一个无参数的构造器!
语法:[修饰符] 构造器名(形参列表)--------构造器名和类名一致
{
//代码
}------构造器体

修饰符:private |protected |public其中之一
构造器名:必须与类名相同。
判断是方法还是构造器:1、看是否有返回值,2、构造器名字与类名一致。
//常量:一旦赋值之后,永远不会改变的值。
static int MAX_SIZE = 200;//常量所有字母大写,单词之间用下划线分隔。

类的作用:
1、定义变量。所有类都是引用类型,所有类都可用于声明变量。语法:类名 变量名;
2、类可以用于调用类方法(static修饰的方法)或者访问类变量(static修饰的变量)
3、用于创建对象:语法: new 构造器(参数)
4、派生子类
对象的作用:
1、调用无static修饰的实例变量
2、调用无static修饰的方法。

this引用:
this可以出现在非static的方法、构造器中.作用如下:
出现非static方法中,this代表了该方法的调用者。
“谁调用该方法,this就代表谁”

-出现在构造器中,this代表该构造器正在初始化的对象。

This.的重要作用是:用于区分方法或者构造器的局部变量(尤其是与成员变量同名时)

Static修饰的方法不能访问不使用static修饰的普通成员即静态成员不能访问非静态成员。
在构造器中访问其他成员变量和方法时可以省略this前缀,但如果构造器中有一个与成员变量同名的局部变量,又必须在构造器中访问这个被覆盖成员变量,则必须使用this前缀。

方法详解:
方法的所属性:
1、方法类似于函数,但与函数不同的是,方法不能独立存在,方法必须定义在类里面。
2、定义在类中的方法,从逻辑上来看,方法要么属于类本身,要么属于该类的一个对象:
如果该方法有static修饰,该方法属于类本身,应该用类调用。
如果该方法没有static修饰,该方法属于对象本身,应该用对象调用。
3、方法不能独立执行(类似于汉语不能没有主语),必须使用类或对象作为调用者
方法一定要有调用者
【规则】如果调用的是同一个类中的方法,可以省略调用者,此时系统会添加默认的调用者,如果该方法无static修饰(即实例方法),默认添加this作为默认的调用者。
如果该方法有static修饰(即类方法),默认添加类作为调用者。

方法参数传递机制:值传递,如果定义方法声明了形参,调用方法时必须传入形参。
Java的参数传递机制:值传递,传入的只是参数值的副本(复制品),并不是参数本身,
如果传递的参数是基本类型,方法中对参数所做的修改,完全不会影响参数本身。
如果传递的参数是引用类型,参数的副本与参数存入的都是同样的首地址,即都指向同一个对象,因此通过参数副本修改对象时,会影响参数本身所指向的对象。
如:基本类型值传递:
public static void swap(int a ,int b)
{
Var tmp = a;
a = b;
b = tmp;
System.out.println(“a : ” + a + “ b: ” + b);
}
Public static void main(String[] args)
{
Var a = 6;
Var b = 9;
Swap(a,b);
System.put.println(“a : ” + a + “ b: ” + b);
}
运行上方程序。会输出:a:9 b:6 a:6 b:9
Main方法中的a,b值都没有改变,只是swap中ab的值交换了,所以,main方法在给swap传入参数时,只是复制了一份副本a,b传递给swap的a,b。书本126页。

引用类型值传递:
Class DataWrap
{
Int a;
Int b;
}
Public class ReferemceTransferTest
{
Public static void swap(DateWrap wp)
{
Var tmp = dw.a;
Dw.a = dw.b;
Dw.b = tmp;
System.out.println(“a : ” + a + “b: ” + b);
}
Public static void main(String[] args)
{
Var dw = new DateWrap();
Dw.a = 6;
Dw.b = 9;
Swap(dw);
System.out.println(“a : ” + a + “b: ” + b);
}
}
执行上方程序,输出:a:9 b:6 a:9 b:6
输出的两行结果一致,但实际引用类型传入的同样是复制品,而不是对象本身,main方法调用swap(),则系统会分别为main()和swap()开辟出两个栈区存放局部变量,调用swap()时,dw作为实参传入swap()方法,即把main()方法中的dw变量的值赋给swap()方法里的dw形参,而main()方法中的dw是一个引用,保存了DataWrap对象的首地址,所以当把dw的值赋给swap()方法后,swap()方法的dw形参也保存了这个首地址。所以两个dw都指向了堆内存的DataWrap对象。无论修改哪个值,另一个dw都会受到相同的影响。因此,引用类型传递参数,系统同样是复制了dw副本传入swap()方法,但dw只是一个引用变量,所以系统复制了dw变量,并未复制DataWrap对象。
如果在swap()方法中加上语句:dw = null;
即让swap()方法中的dw不再指向任何有效内存,main()方法调用swap()方法后,再次访问dw变量的a,b成员变量,依然可以输出🅰️9 b:6 可见main()方法中的dw变量依然有指向DataWraop对象,所以传递参数传递的只是复制品。

变量:P123图5.9

形参个数可变的方法
语法:类型… 形参名 形参个数可变的方法
本质就是数组,上面写法等同于:类型[] 形参名
类型… 写法的好处是:调用方法时更加方便。即可直接传入多个元素,系统会自动将它们封装成数组,也可用数组。缺点是:类型… 这种写法只能作为形参列表的最后一个形参。
【暗示】:一个方法最多只能有一个个数可变的形参。数组形式的形参可以放在任意位置。
书P124页
递归方法:
方法里自己调用自己。-----递归带来了隐式循环。
递归–要避免无限递归,一定要在可能出现的某些情况下,不再调用方法自身,而且该情况必须要出现。
假如:f(1) = 2; f(2) = 5;… f(n) = 2 * f(n+2) - f(n + 1) 计算f(10)是多少?
令Z = n + 2 则f(N - 2) = 2
F(N) - F(N -1);
递归:要保证递归一定能出现递归结束的条件。

方法重载(overload):同一个类中,多个同名方法,形参列表不同
修饰符不同或者返回值类型不同都不算重载,重载需要修饰符、返回值、方法名都相同。
当要确定一个方法。

判断变量:1、首先看位置;2、然后看修饰符(static)

成员变量:可以不需要显示指定初始值,系统可以自动分配初始值,初始值规则与数组元素的初始值完全相同。只要为一个类定义了类变量或实例变量,系统就会在这个类的准备阶段或创建实例时进行默认初始化,程序中,类本身只有一个,程序一定先有类,再有对象(实例)。
类变量:类变量属于类本身,当系统初始化类时,就会为类变量分配空间,并执行初始化

实例变量:实例变量属于对象本身,系统每次创建对象时,都需要为该对象的实例分配空间,并执行初始化。
严格来说:类变量应该由类本身进行访问,实例变量应该由对象进行访问。
java错误语法:允许通过对象来访问类变量——唯一的作用:出考题。但实际上,java依然会将该对象替换成对象所属的类。将对象换成类名就一目了然。
建议:永远只用类调用类变量、类方法。

局部变量:在方 法、形参、代码块中定义,除了形参列表,必须由程序员显式先指定初始值,然后才能使用–否则编译报错,可能尚未指定初始化变量xx,局部变量的作用域很小,只在方法中有效,离开了方法,局部变量立即失效。
代码块的局部变量作用域更小,离开了代码块,代码块局部变量就失效了。
Java允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果要调用被覆盖的成员变量,则可以使用this或类名作为调用者。
P131页:成员变量在内存中运行机制
封装:
面向对象的三大特征:封装、继承、多态。
封装包含2方面的意思:
1、隐藏:将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。
2、暴露:将一些方法暴露出来。让方法来控制对这些成员变量进行安全的访问和操作。
简而言之:封装要求合理隐藏、合理暴露。
访问控制符:private ------>不写控制符(default)-------->protected---------->public
Private(类访问权限):该修饰符修饰的成员,只能在该类中被访问。 彻底隐藏
不写控制符(包访问权限):该修饰符修饰的成员,只能在该类及其类所在包中被访问。部分隐藏
Protected(子类访问权限):该类修饰符修饰的成员,只能在该类、及其该类所在包、该类的子类中被访问。 部分暴露
Public(公共):该修饰符修饰的成员,可以在任意地方被访问。 彻底暴露
private ------>不写控制符(default)-------->protected---------->public
当前类 √ √ √ √
同一个包 √ √ √
子类 √ √
任意位置 √
指定原则
1、成员变量(实例变量)通常用Private修饰,为了隐藏实现细节
2、为每个成员变量提供getter、setter方法,用于控制该成员变量的方法。
3、需要暴露的方法,通常用public修饰。
4、如果希望一个方法主要用于被子类重写,用protected修饰。

不同公司完全可以定义同名的类,为了解决不同公司、不同项目的类名重复的问题。java引入”包”的机制——就是在类名添加一个前缀,如:疯狂java.User
Java定义包:
在源代码中用package 包名;
将生成class文件要放在对应的文件结构下。

包名的命名规范:语法要求:只要是标识符,专业要求:推荐用公司域名倒写,再加项目名如:org.fkjava.项目名

Java bean规范:如果一个类的每个实例变量都使用private修饰,并为每个实例变量都提供了public修饰的setter和getter方法,name这个类就是一个符合JavaBean规范的类。

包:包名全部是小写字母
有包的情况,必须使用语法:java -d . 文件名 会自动生成对应文件夹包,并将生成的class文件放到对应的文件夹包中。
【备注】:一旦为类指定了包名之后,使用该类时应该用完整类名:包名 + 类名 如:
Java org.fkjava.javase.Hello
为Java类添加包必须在Java源文件中通过package语句指定,单靠目录名是没法指定的,Java的包机制需要两个方面保证:1、源文件里使用package语句指定包名,package必须作为源文件的第一条非注释性语句2、class文件必须放在对应的目录下

导入包
Import:作用:可以省略写包名,如果不用Import,每次用类时都需要使用:包名 + 类名的形式。
语法一:import 包名.类名——每次导入一个类
语法二:import 包名.* ——导入指定包下面的所有类,此处的*只能代表类名。
java程序默认已导入java.lang包下所有类,如:String、System
当需要使用两个包中同名的一个类时,此时如果导入了两个包,程序无法辨别使用的是那个包中的类,则需要用完整的 包名.类名 去使用该同名类。

静态导入----------------java1.5才引入
:Import static 作用:可以省略写类名。用于导入指定类下面的所有静态成员,
导入之后,可以省略写类名。

语法一:import static 包名.类名.静态成员名——每次只导入一个静态成员。
语法二:import static 包名.类名.*——一次导入指定类下面的所有静态成员。

Java源程序的结构:
一条package语句
N条import语句
N个class定义------最多只能有一个Public类

Java的常用包:
Java.lang:包含所有java语言的核心类,String、System、Math、Thread,系统默认自动导入该包下的所有类。无需import导入
Java.util:包含Java大量工具类/接口和集合框架类/接口,如Arrays、List、Set等
Java.net:包含Java网络编程相关的类/接口
Java.io:包含Java输入/输出编程相关的类/接口
Java.text:包含Java格式化相关的类
Java.sql:包含Java进行JDBC数据库编程的相关类/接口

构造器详解:最大用处:创建对象时执行初始化
构造器规则:
1、构造器用于初始化对象
2、构造器必须用new 来调用构造器。语法:new 构造器名() 这样就可以返回一个初始化完成的对象
3、如果不为一个类提供构造器,系统会自动为该类提供一个无参数的构造器。

创建对象过程:当调用构造器时,系统会先为该对象分配内存空间,并为这个对象执行默认初始化——这些操作在构造器执行之前就完成了,也就是说,在系统执行构造器的执行体之前,系统已经创建了这个对象,只是该对象只能在该构造器中通过this引用,当构造器执行体执行结束后这个对象才作为构造器的返回值被返回,通常会赋给一个引用类型变量,让外部程序可以访问该对象。
构造器重载:一个类中可以定义多个构造器(构造器名和类名相同,因此构造器名必然相同),必须要求形参列表不同。——这就是构造器重载。
this引用:this紧跟一个. 如:this.name this.walk();
this调用:this紧跟圆括号,this(参数) this调用代表调用同一个类中重载的构造器——this调用只能出现在构造器的第一行!

继承:
封装、继承、多态——是面向对象的三大特征。
理解继承:苹果继承了水果、老虎继承了动物。
java的继承,是一种类与类之间的关系,是一种由一般到特殊的关系,子类是一种特殊的父类,子类 — 小类,父类—大类。子类实例完全可以当成父类实例来使用。
Java的继承,用的是extends单词,扩展的。
父类(超类、基类) 子类(派生类)

继承语法:
[修饰符] class 子类名 extends 父类
{
}
说明:java是单继承,只能有一个直接父类。
如果不显式继承父类,java默认继承Object类(JDK系统提供的类)
一切都是object
继承的好处:代码复用
子类继承父类:子类只能从被扩展的父类获得成员变量、方法和内部类(包括内部接口、枚举),不能获得构造器和初始化块。
方法重写(override)
子类发现父类的方法不适合自己的时候,就要重写父类方法。
方法重写口诀:2同2小1大,2同:方法名相同、形参列表相同。2小:返回值类型相同或更小,声明抛出的异常相同或者更小 1大:访问权限相同或者更大。(即方法前的修饰符相同或范围更大)
@Override作用是报错,要求被修饰的方法必须重写父类方法,否则就报错。
放在子类重写父类方法上面。
Super限定:与前面this引用非常相似,super用于限定访问父类定义的实例变量或实例方法。语法:super.父类定义的实例变量 super.父类定义的实例方法(参数)

如果要在子类方法中调用父类方法中被覆盖的方法,使用super或父类名调用。
如果父类方法具有private修饰,则该方法对子类是隐藏的,子类无法访问也无法重写该方法。如果子类定义一个与父类private方法具有相同的方法名、相同的形参列表,相同的返回值类型,依然不是重写,只是在子类中重新定义了一个新方法。
父类方法和子类方法之间也有可能发生重载:子类继承会获得父类方法,如果子类定义一个和父类方法有相同的方法名但形参列表不用的方法,就会形成父类方法和子类方法的重载。

当程序创建子类对象时,系统不仅会为该类中定义是实例变量分配内存,也会为它从父类继承得到的所有实例变量分配内存,即使子类定义了与父类中同名的实例变量,此时,子类中定义的变量只是隐藏父类中定义的变量,并不是完全覆盖,因此,系统在创建子类对象时,依然会为弗雷中定义的、被隐藏的变量分配内存空间。
子类调用父类的构造器:
子类构造器一定要调用父类构造器一次——有且仅有一次
1、如果子类构造器没有显式调用父类构造器,系统会自动在子类构造器的第一行先调用父类无参数的构造器。即:如果父类写了构造器,那就没有无参数的构造器,则子类如果不显式调用父类构造器,就会报错。
2、子类构造器的第一行代码使用this显示调用本类中重载的构造器,系统根据this调用里传入的实参列表调用本类中的另一个构造器,执行本类中另一个构造器时也会先调用父类无参构造器。
3、子类构造器的第一行显式使用super调用来调用父类的构造器。Super调用一定是调用父类的构造器,而this调用是调用当前类的构造器,都只能出现在构造器的第一行。
Super调用和this调用不可能同时出现。
不仅如此,执行父类构造器时,系统还会向上执行其父类构造器,以此类推

Super限定:super紧跟一个. 如:super.name super.walk()
super调用:super紧跟圆括号 super(参数);

【推论备注】:如果父类没有无参的构造器,子类构造器必须显式调用父类指定的构造器!

多态:同一个类型的多个实例、在执行同一个方法,呈现出多种的不同的行为特征——这就叫多态。
多态的形成:
Java执行方法时,方法的执行是动态绑定实际类型的方法的:方法总是执行该变量实际所指向对象的方法。
与方法不同的是,对象的实例变量则不具备多态性,声明变量时的编译时类型是什么,则使用实例调用的实例变量就是编译类型所拥有的实例变量。
变量的类型:
编译时类型:声明该变量时指定的类型,在java程序的编译阶段,java编译器只认编译时类型。所以对于编译器而言,声明的变量是什么类型,则属于该类型的方法就可以调用,即引用变量只能调用声明该变量时所用类里包含的方法,如Object p= new Person(),p只能调用Object类的方法,不能调用Person特有的方法。

运行时类型(实际类型):该变量实际所引用的对象的类型。
向上转型:子类对象可以直接赋值给父类变量。自动完成。

向下转型(强转):父类变量赋值给子类变量时,要强制转换。语法格式:(类型)变量名;

强转运算符的注意点:
基本类型之间的转换只能在数值类型之间进行:包括整型、字符型、浮点型
1、强转运算符只能在编译类型具有父子继承关系的变量之间进行转换,否则编译报错。“不兼容的类型”
2、如果在编译类型具有父子继承关系的变量之间进行转换时,如果被转变量的实际类型,不是要转的目标类型,程序在运行时就会引发”classcastException(类型转换异常)”

instanceof运算符:
作用:为了避免出现classcastException(类型转换异常),java就增加了这个运算符。
语法: 变量名instanceof 类型——即当前面的变量名所引用的对象是后面类或子类的实例时,该运算符返回true,否则返回false.
Instance只有在编译类型具有继承关系之间进行判断,否则编译错误,“不兼容的类型”
该运算符可以保证,被强转的变量确实是可转换的,才进行转换,从而避免classcastException.

组合:使用A类定义为B类的成员变量,并用private修饰,此时就可以在B类中生成A类的实例,在B类中使用A类实例调用A类方法。

初始化块:语法:
[修饰符]{
各种语句
}
初始化块是没有名字的,修饰符只有一个,static,有static叫类初始化块(静态初始化块),没有static的叫实例初始化块(非静态初始化块)
实例初始化块:没有static修饰,实例初始化块是“假象”;一个类在编译之后,实例初始化块就会消失——实例初始化块的所有代码会被还原到每个构造器的开始部分。
编译后,使用javap -c 类名 反编译分析类的.class文件的流程
实例初始化块的作用:将多个构造器前面部分相同的代码可以提取到实例初始化块中。
实例初始化块执行:只要程序调用构造器创建对象,程序总会先执行实例初始化块——因为实例初始化块编译后被还原到每个构造器的所有代码之前。(但在This()调用或者super()父类调用之后,因为这两种调用总是在构造器的第一行.)

定义实例变量时指定的初始值,也是“假象”——指定的初始值,编译之后就变成构造器所有代码之前一条赋值语句。
实例初始化块的语句要还原到构造器的所有代码之前;定义变量指定的初始值,也要还原到构造器的所有代码之前,这二者还原之后的顺序按照它们在源代码中的顺序。

类初始化块:有static修饰,负责对类执行初始化,当程序第一次【主动】使用该类时,系统会为该类分配内存空间、并执行初始化(调用类初始化块)
只要使用该类:基本都算主动使用,除了仅使用该类声明变量。
类初始化块执行:程序第一次【主动】使用该类时,会执行类初始化块。
在程序运行时,该类初始化块只会执行一次。

执行次数 执行先后 何时执行
类初始化块 1次 先 第一次主动用该类
实例初始化块 N次 后 每次调用构造器

定义类变量指定的初始值,也是“假象”,——指定的初始值,编译之后就变成类初始化块中的一条赋值语句,但到底是在类初始化块的代码之前,还是代码之后,取决于它在源代码中的顺序。
即首次使用类创建对象时执行顺序为:先执行顶层父类的类初始化块依次执行完所有类初始化块、再执行顶层父类的实例初始化块、构造器,然后依次是自雷实例初始化块、构造器依次执行。

观点:初始化任何类之前,一定先从Object开始初始化,依次初始化它所有祖先类,最后才到他自己。创建任何对象之前,一定是从Object构造器开始执行,依次执行它所有祖先类的构造器,最后才执行它自己的构造器。

包装类:
Java有8个基本类型byte、short、int、long、float、double、char、boolean,这8个基本类型都不能当成对象使用,而且也不能接收Null值。
为了解决上面问题,Java为8个基本类型提供了对应的包装类——可将他们包装成对象。
byte----------Byte
short -------Short
int------------Integer
long---------Long
char---------Character
float---------Float
double--------Double
boolean-------Boolean
自动装箱(boxing):基本类型的值可以自动当成包装类的实例使用。
自动拆箱(unboxing):包装类的实例可以自动当成基本类型的值使用。
【建议】:做项目时,通常来说建议使用包装类来声明变量。好处:反正基本类型能做的,它都可以做。它还可以当成对象使用。还可以接受null。

包装类实现字符串和基本类型变量之间转换的方法: parseXxx
可将字符串转换成对应的类型值
NumberFormatException:要转的字符串不符合数值格式,将会引发异常。
包装类比较:
1、包装类的实例可以与数值类型的值进行比较:比较时只是取出包装类实例所包装的数值进行比较。
2、两个包装类实例进行比较:只有两个包装类引用都指向同一个对象时才会返回true
包装类比较值大小可以使用:对应包装类.compare(xxx val1,xxx val2)方法比较两个基本类型值的大小。
Boolean比较时true > false;参数1大于参数2返回1,小于返回-1,等于返回0

当程序对Integer使用自动装箱时,它有一个缓存机制,它会缓存-128~127之间的对象。
即-128~127之间的对象都被缓存起来,只要自动装箱的数值在此范围之内,默认会使用缓存的对象,此时两个包装类相等,如果自动装箱的数值在范围之外,则对象不会缓存,会重新创建对象,因此数值不相等。如:
System.out.println(Integer.valueOf(2) == Integer.valueOf(2)); //输出true
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));//输出false

两个要被重写的方法:
toString()方法
程序打印对象,或把对象自动转换字符串时,实际上用的是该对象的toString()方法的返回值
【默认的toString】Object提供的toString返回:类名@hashCode方法返回值(证明:可以在类中重写hashCode方法,返回一个具体数值,再次打印对象会输出:类名@hashCode方法十六进制的返回值)如果没有重写hashCode方法,则原hashCode返回的是内存地址。
重写toString方法,返回该对象的“自我描述”信息(方法重写前必须加@Override)
//任何对象 + “”,就会变成字符串
String str = ap2 + “”;
所有java对象和字符串连接运算时,系统自动调用java对象的toString()方法的返回值和字符串进行连接运算。

==判断两个变量,如果两个变量是基本类型变量,且都是数值类型(不要求数据类型严格相同),则只要两个变量的值相等就返回true。
如果判断两个引用变量,要求两个引用变量指向同一个对象时,才会返回true,且不能比较类型上没有父子关系的两个对象。
常量池专门用于管理在编译时被确定并被保存在已编译的.class文件中的一些数据,包括类、方法、接口中的常量和字符串常量。

equals()方法:
【默认的equals】:Object提供的equals方法,判断两个对象相等的标准与==是完全一样的。都需要两个变量指向同一个对象才相等。String已经重写了Object的equals()方法,String的equals()方法判断两个字符串相等的标准是:两个字符串所包含的字符串序列相同就返回true,否则返回false。
重写equals方法,根据业务规则来判断提供两个对象相等的标准。实际项目中,用来作equals比较的关键成员变量,通常并不需要使用全部的成员变量。只要用它们关键的成员变量即可。
Static:
并不是静态的意思,static是类的意思,有static修饰的成员属于类成员,没有static的成员属于实例成员。static不能修饰局部变量。·局部变量不属于五大成员,因此不能用static修饰,static也不能修饰外部类——外部类不属于成员,也不属于任何人。
Static可修饰的成员:成员变量、方法、初始化块、内部类,不能修饰构造器
Static成员(成员变量、方法、初始化块、内部类)—属于类成员,所有类成员都只能用类名调用。Java允许通过对象来调用类成员,其实没有意义,
Static考点:
静态(static)成员不能访问非静态成员,非static成员可以访问static成员。
Static成员(4种)不能访问非static成员(5大成员都可以);

单例模式:
设计模式:对于一批经常出现的设计场景,前人总结出来的比较成功的设计—这既是设计模式。后人应该学习、并模式,从而提高代码质量。如:
单例模式:在某些场景下,某些类只需要(只能)创建一个实例。比如:系统的窗口管理器、
数据库引擎访问点、java程序所在JRE环境等都只要产生一个实例。此时就应该才用单例模式。
设计单例实例:
1、private隐藏构造器—避免被创建实例
2、暴露一个static方法,该访问用于创建实例(因为构造器隐藏,不能创造实例,所以没有对象可调用方法,因此,用static修饰,用类调用方法创造实例。)该方法还需要保证,该类只会产生一个实例(如果已经创建过实例,赋值给以该类成员变量,若该类成员变量为null,则创建一个实例,若不为null,则直接返回实例。)

final:可以修饰变量(各种变量)、方法、类。
Final 与 abstrast是互斥,永远不能同时出现!
final修饰变量:该变量被赋值初始值之后,不能被重新赋值!Final修饰的变量必须被赋值,且只能被赋值一次。
非final修饰成员变量,程序员可以不显式指定初始值,系统会为之分配默认的初始值,初始值分配规则与数组元素的初始值分配规则完全相同。
Final修饰成员变量:程序员必须显式指定初始值
final实例变量:必须显式指定初始值,只能在以下三个位置的其中之一指定:
1、定义时指定初始值
2、实例初始化块
3、每个构造器都需要显式指定一次初始值
上面三个为之的本质其实只有一个,构造器(编译后都会还原到构造器中)

final类变量:必须显式指定初始值,只能在以下两个位置的其中之一指定:
1、定义时指定初始值
2、类初始化块
上面2个位置的本质其实只有一个,类初始化块(编译后都会还原到类初始化块中)
不要在初始化之前访问final成员变量,final成员变量在显式初始化之前不能直接访问,但是可以通过方法来访问此时将看到系统将final成员变量默认初始化为0(false、null)等。
final局部变量:
非final修饰的局部变量:程序员必须先显式指定初始值,然后才能使用。
final修饰的局部变量:程序员也必须先显式指定初始值,然后才能使用,final局部该变量不能重新赋值。

final修饰的是引用类型的变量:
final只保证该引用变量本身不会被重新赋值,该变量所引用的对象完全可以被修改。

final修饰的“宏替换”的变量:
如果一个变量满足以下3个条件:
1、变量有final修饰
2、声明变量的时候指定了初始值。
3、变量的初始值可以在编译的时候确定下来(初始值的表达式中不含有变量、方法调用等)
这个变量就会消失,所有出现该变量的地方,在编译的时候就会替换成该变量的值。
即编译器会把程序中所有用到该变量的地方直接替换成该变量的值。
Java会使用常量池来管理曾经用过的字符串直接量,例如:var a = “java”语句之后,常量池就会缓存一个java字符串,如果再var b = “java” 系统将会让b直接指向常量池的“java”
字符串。
如果变量中的赋值表达式有其他变量,若想当成宏变量,可以将表达式的变量用final修饰。如:s1 = “java” s2 = “疯狂” s3 = s1 + s2 如果用final修饰s1和s2,则s3中赋值表达式的s1和s2可以直接用直接量替换,s3可以当成宏变量进行处理。
final修饰方法:表明该访问不允许被子类重写—该方法可以被重载、也可以被调用。
【备注】:private修饰的方法已经被隐藏在类的内部,子类无法访问该方法,因此不可能被重写,final修饰private方法纯属多余,不过java允许这样的语法。如:
class Foo
{
//private方法已经被隐藏在该类的内部,不可能被重写。
//子类无法访问该方位,因此不可能被重写,所有final在这里没有意义
private final void test()
{
System.out.println(“test方法”);
}
}
public class FinalError extends Foo
{
//@Override 如果加这一行,报错。发现下面代码根本没有重写test方法,
//下方只是子类定义了一个test方法。
public void test()
{
System.out.println(“重写test方法”);
}
}

final修饰类:表名该类不能派生子类。
Jdk里很多类都是final:String、Math、System

不可变类:创建该类的实例后,该实例的实例变量是不可改变的。规则:
1、使用private和final修饰符来修饰该类的成员变量,提供带参数的构造器(或返回该实例的类方法),用于根据传入参数来初始化类里的成员变量
2、仅为该类提供getter方法,不提供setter方法,因为普通方法无法修改final修饰的成员变量
3、如果有必要,重写Object类的hasCode()和equals()方法,equals方法根据关键成员作为两个对象是否相等的标准,当equals()方法判断为相等的对象,hasCode()也相等

abstract(抽象):
只能修饰2个东西:方法、类,修饰方法叫抽象方法、修饰类叫抽象类。

抽象类:有得有失
抽象类与普通类的区别只有四个字:有得有失
有得:得到一个新功能:抽象类可以拥有抽象方法。
有失:抽象类是去了一个功能:创建对象。(可以有构造器,只是不能创建对象)

抽象类的主要作用:派生子类(和final互斥,final不能派生子类,abstract一定要派生子类)
子类构造器一定要调用父类构造器一次,因此抽象类必须有构造器。

抽象方法:只有方法签名,没有方法体的方法。如:public abstract void move();
普通类不能有抽象方法,只有抽象类才能存在抽象方法。抽象方法没有方法体,不能调用,所以一定要给子类去重写。(final是一定不允许重写,abstract一定要重写,所以互斥)
抽象方法:一定要交给子类去实现,否则不能调用。
抽象类的作用:
1、定义变量,只能用它的子类的实例,向上转型进行赋值。
2、调用类方法和类变量
3、派生子类—主要目的
抽象类派生子类:
【规则】:子类要么重写抽象父类中所有抽象方法,要么子类也只能是抽象的。(因为如果没有重写,则子类默认继承了父类的抽象方法,必须是抽象类才能拥有抽象方法。)

接口:
接口相当于一种彻底抽象的类(但不能说是一个类)
接口体现的是一种规范—要暴露出来供大家遵守的,所以接口里所有东西都用public修饰,不管写与不写,始终有Public修饰,通常都不写。
定义接口
[修饰符] interface 接口名 extends 父接口1,父接口2…
{
零个到多个成员变量定义——只能是静态常量,变量名大写,字母间用下划线分隔 (MAX_SIZE)默认有public static final修饰,必须定义时指定默认值
零个到多个抽象方法——即普通方法,没有方法体,默认有public abstract修饰,可省略
零个到多个内部类、接口、枚举定义——默认有public static修饰
零个到多个默认方法定义——默认有public修饰,可省略,但必须添加default修饰
零个到多个类方法定义——默认有public修饰,可省略,必须加static修饰
(零个或多个私有方法——Java 9才可以使用,可以是静态也可以是非静态方法)
}
接口修饰符只能是public或不写,一个接口可以继承多个父接口,但不能继承类。
接口是一种规范:不能有构造器和初始化块,成员变量只能是静态常量。

默认方法:就是实例方法。默认方法与原来的抽象方法地位完全相同,用default修饰。
使用default修饰,有方法体,可以通过对象直接调用。
接口是多继承的

修饰符:public、接口是彻底抽象,不能有final,接口本身彻底抽象了,所以也不能有abstract。
接口名:命名规范基本等同于类名。接口一般推荐使用形容词。添加able就是形容词。
成员变量:只能有常量,所有常量都有Public static final修饰,通常不写。final修饰的变量必须指定初始值,且有static修饰,接口没有类初始化块,所以定义时必须要指定初始值。
Int MAX_PRESERVE = 100;

抽象方法:默认有Public abstract,通常不写,如:void test();
类方法:前面默认只添加public,通常不写
static void taste()
{
System.out.println(“info类方法”);
}
//为了给实例方法添加方法体,必须要default修饰
//default相当于将abstract删除,前面默认只添加public,通常不写
default void test()
{
System.out.println(“info类方法”);
}
接口的作用:
1、定义变量,只能用实现类的实例来赋值(向上转型)
2、调用类方法或访问类变量
3、派生实现类—主要作用

实现接口:
[修饰符] class 类名 extends 父类 implements 父接口1,父接口2,…
{
//五大成员
}
【推论规则】:实现类要么重写接口中的所有抽象方法,要么实现类也是抽象的。
重写接口中的方法,只能用public修饰且不能省略,因为接口中所有方法都有public
Java 9 为接口增加的private方法:
语法:java 9之后,接口中的private方法也可以定义方法体。
Private方法:当两个默认方法(或类方法)中包含一段相同的实现逻辑时,程序必然考虑将这段实现逻辑抽取成工具方法,而工具方法是应该被隐藏的,这就是私有方法。
功能:如果在接口定义了private方法,那么接口中至少有一个default方法会调用该private方法,否则该private没有太大的意义了。
Java 8的defualt方法的本质就是实例方法—接口中default方法在编译之后,default就被去掉了
由于接口中的default方法本质就是实例方法,
那么多个实例方法之间很有可能出现“公共部分”,这个“公共部分”就应该被抽取到工具方法中,而工具方法又希望被隐藏,—用private修饰。所以从java 9开始,java就为接口增加了private方法——这就是private方法的由来。
Private方法的本质是实例方法,且是工具方法:为接口中多个default方法公共部分提供的支持。
接口和抽象类:
相同:
1、接口和抽象类都不能被实例化,用于被其他类实现和继承
2、接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类必须实现这些抽象方法。
不同点:
1、接口作为系统与外界交互的窗口提现的是一种规范,一个程序使用接口,接口是多个模块间的耦合标准,多个程序使用接口,接口是多个程序间的通信标准。而抽象类作为系统中多个子类的共同父类,提现的是一种模板式设计。
2、接口只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供实现。
抽象类完全可以包含普通方法。
3、接口只能定义静态常量,不能定义普通常量,抽象类普通成员变量和静态常量都可以。
4、接口不包含构造器,抽象类可以包含构造器,该构造器不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
5、接口不能包含初始化块,抽象类可以完全包含初始化块。
6、一个类最多只能有一个直接父类,包括抽象类,但一个类可以直接实现多个接口实现多个接口弥补java单继承的不足。

内部类:放在类体里定义的类,该类就属于内部类,包含内部类的类就被称为外部类(宿主类)。
内部类与外部类(顶层类)的区别:
1、内部类可以多如下3个修饰符:static、private、protected
2、非静态内部类不能拥有静态成员,除非是常量。
3、内部类可以直接访问外部类的私有成员,但静态内部类不能访问外部类的非静态成员。

内部类存在的意义:当某个类的实例必须要依附于另一个类的存在而存在,此时可使用内部类,内部类可以提供更好的封装。内部类编译后生成的文件名:外部类名$内部类名.class
区分变量:内部类可以直接访问外部类的成员变量,但如果内部类的方法、或者内部类的成员变量与外部变量的成员变量重名时,就需要进行区分:
外部类.this.实例成员:调用外部类的实例成员,即外部类的this(实例)的实例成员。
This.实例成员:实例的成员变量
在非静态内部类里可以直接访问外部类的private成员,这是因为非静态内部类对象里保存了一个它所寄生的外部类对象的引用(当调用非静态内部类的实例方法时,必须有一个非静态内部类实例,非静态内部类实例必须寄生在外部类实例里)。
总而言之,非静态内部类对象必须寄生在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄生其中,如果存在一个非静态内部类对象,则一定存在一个被它寄生的外部类对象,但外部类对象存在时,不一定寄生了非静态内部类对象,因此,外部类对象访问非静态内部类成员时,可能非静态普通内部类根本不存在,而非静态内部类对象访问外部类成员时,外部类对象一定存在。
底层理解:非静态内部类就是无stitic修饰的成员,即外部类的实例成员,所以他必须寄生在外部类的实例当中。

Java中不允许在非静态内部类里定义静态成员,静态内部类可以包含静态和非静态成员。
静态内部类不能访问外部类的实例成员,只能访问外部类的类成员,静态内部类的实例方法也不能访问外部类的实例成员,只能访问静态成员。静态内部类不是寄生在外部类的实例当中,而是寄生在外部类的类本身,当静态内部类对象存在时,并不存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的类引用,没有持有外部类对象的引用。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,会引起错误。
底层理解:静态内部类就是有stitic修饰的静态成员,即外部类的类成员,所以他必须寄生在外部类当中。且静态成员不能访问非静态成员,即无法访问外部类的实例成员。

静态内部类是外部类的一个静态成员,因此外部类的所有方法和初始化块都可以使用静态内部类来定义变量、创建对象等。
外部类依然不能直接访问静态内部类成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员

Java还可以在接口里定义内部类,默认使用public static修饰,即只能是静态内部类。
使用内部类:
1、在外部类的里面使用内部类:基本上使用内部类与使用其他类没什么区别 — 唯一注意:静态成员不能使用非静态的内部类创建实例。
2、在外部类的外面使用静态内部类
在外部类的外面使用静态内部类:该内部类一定不能用private修饰
功能如下:
A、声明变量:外部类.内部类 变量名
B、创建对象:new 外部类.内部类构造器(参数)
因为静态内部类是寄生在外部类当中,因此,创建静态内部类对象时无须创建外部类对象,在外部类以外创建静态内部类对象可以直接调用静态内部类的构造器语法如下:外部类名.内部类名 变量 = new 外部类名.内部类构造器();

C、调用类方法或者访问类变量:外部类.内部类.类变量 外部类.内部类.类方法(参数)
D、派生子类:外部类.静态内部类
静态内部类的实例的宿主是外部类本身,因此基本无需开发者操心——只要主动使用该 类,该类就被初始化出来
3、在外部类的外面使用非静态内部类(高难度),内部类,也叫寄生类,static内部类的实例,要寄生在外部类的类本身中,非static内部类的实例,要寄生在外部类的实例中。这里的外部类的本身和外部类的实例就属于宿主。(即非静态内部类的对象必须寄生在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象)语法:
Outerclass.Innerclass inner = new Outerclass().new Innerclass()
上面语法也可以分成三步:
Outerclass.Innerclass inner;
Outerclass out = new Outerclass();
Outerclass.Innerclass inner = out.new Innerclass();
可以看出,非静态内部类对象必须寄生外部类对象中,非静态内部类构造器必须由外部类对象调用。
外部类:也叫宿主类
功能如下:
A、声明变量:外部类.内部类 变量名
B、创建对象:宿主.new 非静态内部类的构造器(参数);
语法一:InstanceInner ii = new InstanceInner();
//创建对象:宿主.new 非静态内部类构造器(参数);
InstanceInner.Inner in = ii.new Inner(2);
语法二:InstanceInner.Inner in2 = new InstanceInner().new Inner(2);
C、调用类变量(只能是常量):外部类.非静态内部类.类常量
D、派生子类:外部类.非静态内部类
派生子类时,子类构造器总会调用父类的构造器,因此在创建非静态内部类的子类时,必须保证让子类构造器可以调用非静态内部类的构造器,调用非静态内部类的构造器时,必须存在一个外部类对象.子类构造器第一行使用调用语法:new Outerclass().super(形参列表);
因此要创建一个非静态内部类的子类对象,必须要先创建一个外部类对象。非静态内部类对象必须有一个对外部类对象的引用,所以子类也必须持有对外部类对象的引用。但区别是,创建两种对象时传入外部类的方式不同:创建静态内部类的对象时,通过外部类对象调用new关键字,创建非静态内部类的子类对象时,使用外部类对象作为调用者来调用非静态内部类的构造器。所以只要有费静态内部类对象存在,或者是非静态内部类的子类对象存在,则一定存在与之对应的外部类对象。
重点在于:子类构造器的第一行一定要用如下语法:
宿主.super(参数); (new Outerclass().super(形参列表)😉
非静态内部类(包括其子类)的实例的宿主是外部类的实例,因此必须由程序员先创建外部类的实例作为宿主。

外部类定义子类时,不能重写其父类中的子类:父类中的内部类名为:父类名.内部类名
子类中的内部类:子类名.父类名.内部类名 所以他们不可以重名,不能重写。

局部内部类(了解即可,很少使用):局部内部类放在方法中定义,局部内部类只能在方法里使用,离开该方法局部内部类失效,局部内部类也只能用final修饰。
局部内部类的类文件名为:外部类$N内部类.class 如果不同方法方法中有多个同名类,则按照出现的顺序,第几个出现则N就为几,如果没有同名类,则N都为1.

匿名内部类:
非常常用!尤其是安卓开发,几乎普遍存在的。
匿名内部类:没有名字的类,因此在定义类要立即创建实例,以后不能复用该类。
语法:new 父类构造器(参数) |接口()
{
//类体部分,完全可以定义类的五大成员——但构造器不行,关键是不知道构造器的名字。通常类体部分只要实现抽象方法即可
};
从上面语法可以看出:
1、匿名内部类必须要显式继承一个父类或者实现一个接口。不能同时实现多个接口。
也不能继承父类并实现接口。
2、匿名内部类不能是抽象类。因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此必须实现抽象父类或接口中所有的抽象方法。
3、不能定义构造器,因为没有类名,匿名内部类实现接口,只有一个隐式的无参数的构造器,但如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器(相同的形参列表),匿名内部类可以定义初始化块,通过实例初始化块完成构造器需要完成的事。
匿名内部类还可以重写父类方法中的普通方法
Java8之前,要求被局部内部类、匿名内部类访问的局部变量必须使用final修饰,java8以后,如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰,即使没写final,也必须按照有final修饰的 方法来用。即不能被二次赋值。

函数式接口:只有一个抽象方法的接口就叫函数式接口。(其他默认方法、类方法可以有多个。)
@FunctionalInterface:用于修饰函数式接口,作用与@Override类似,都是用于”报错”。保证接口是函数式接口,如果有超过一个抽象方法,报错。

Lambda表达式:本质就是函数式接口的匿名内部类。——只保留匿名内部类中所实现的抽象方法的形参列表和方法体部分。并在参数列表和方法体之间用->隔开。
语法:父类名 变量名 = (形参列表) ->{ 形参列表为父类方法抽象方法的形参列表
执行性语句
};
Lambda表达式的简化:
1、通常来说,写形参列表时都省略形参类型如:Movable mv = (name,age) ->{}
2、如果形参列表只有一个参数,形参列表的圆括号可以省略。如:Movable mv = distance ->{}
3、如果方法体只有一条代码,方法体的花括号可以省略。如:Movable mv = distance ->语句
4、如果方法体只有一条代码,且代码是return语句,那么可以省略return关键字。

Lambda表达式的目标类型必须是函数式接口,为了保证Lambda表达式目标类型是明确的函数式接口,常见有如下三种形式:
1、将Lambda表达式赋值给函数式接口类型的变量
2、将Lambda表达式作为函数式接口类型的参数传递给某个方法
3、使用函数式接口对Lambda表达式进行强制类型转换。如:Object obj = (Runnable)()->{}
同样的Lambda表达式的目标类型可能是变化的,如用Lambda表达式实现两个形参列表相同的两个函数式接口。
如果程序需要对Lambda表达式的形参添加注解,此时就不能省略Lambda表达式的形参类型——因为注解只能被放在形参类型之前。
Lambda表达式的方法引用和构造器引用——只有当方法体只有一条代码时才可使用。
本质是,进一步省略,完全省略了形参列表和箭头(->)
1、引用类方法
原本简化写法:(形参列表) -> 某个类.某个方法(形参列表);如:
Fn fn1 = (d1,d2) -> Math.hypot(d1,d2);
进一步简化后:某个类.某个方法; 如:
Fn fn2 = Math::hypot;
2、引用实例方法
原本语法:(参数1,参数2…) -> 参数1.某个方法(传入的形参); 如:
Converter c1 = (str,start,end) -> str.substring(start,end);
进一步简化:参数1的类型::某个方法; 如:
Converter c2 = String::substring;
3、引用特定对象的实例方法:
原本语法:(参数1,参数2…) -> 某个对象.某个方法(形参列表); 如:
Converter c1 = (start,end) -> “我爱java编程”.substring(start,end);
进一步简化:某个对象::某个方法; 如:
Converter c2 = “我爱java编程”::substring;
4、构造器引用:
原本语法:(参数1,参数2…) -> new 某个类(形参列表); 如:
Test t1 = (a,b,c) -> new Date(a,b,c);
进一步简化:某个类::new;
Test t2 =Date::new;

Lambda表达式与匿名内部类的联系和区别:
Lambda表达式时匿名内部类的一种简化,可以部分取代匿名内部类的作用,
相同点:
1、Lambda表达式和匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变量(包括实例变量和类变量),与匿名内部类相似,Lambda表达式访问的局部变量会相当于有一个隐式的final修饰,即使该局部变量没有final修饰,也必须按照有final修饰的 方法来用。即不能被二次赋值。
2、Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。

区别:
1、匿名内部类可以为任意接口创建实例—不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可,但Lambda表达式只能为函数式接口创建实例。
2、匿名内部类可以为抽象类甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例
3、匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法,但Lambda表达式花括号内的代码块不允许调用接口中定义的默认方法,只能通过Lambda表达式创建的对象赋值给变量,用变量调用默认方法。

枚举:枚举是实例数量已经固定的类
现实成活中,很多类的实例数量是固定的,开发者不能随意创建实例。
推论:枚举很方便地实现单例模式。
语法格式:
[puiblic] abstract |final enum 枚举名 ----abstract 和final不允许写,系统会默认给一个。
{
//第一行列出所有实例——枚举以后就只能用这些实例。
//同样可以定义类的5大成员
}

与普通类的语法区别:
-枚举默认已经继承了Enum类(Object的子类),因此枚举不能继承其他类。
-枚举类默认要么是final类,要么是abstract(抽象)类,final|abstract修饰符无需自己写,如果枚举类没有抽象方法,系统自动添加final,如果添加了抽象方法,系统自动添加abstract。
-枚举的构造器只能用private修饰,构造器不写修饰符,系统也会默认添加private修饰符。因此,枚举不能派生子类。
-枚举要求在第一行显式列出所有的实例,否则这个枚举类永远不能产生实例,系统会自动在实例前添加public static final修饰,无需显式添加

定义枚举之后,枚举默认自动拥有如下方法:
static Season[] values():该方法返回所有的枚举实例,如:
For (var s : 枚举名.values())
{
System.out.println(s);
}
static Season valueOf(String name):该方法根据枚举名字返回枚举实例。
String name(); :该方法返回枚举实例的名字
Int ordinal(); 该方法返回枚举实例的序号。

枚举可用于:
1、定义变量
2、调用类方法或者类变量。
建议枚举的成员变量都是用private final修饰,修饰后必须在构造器里为这些成员变量指定初始值(也可以在定义成员变量时或初始化块中指定,但这两种情况不多见)。
Switch表达式的类型可以是:byte、short、int、String(java7) 枚举

枚举与构造器
在枚举中列出枚举值时,实际上就是调用构造器创建枚举类对象,只是这里无需使用new关键字,也无须调用构造器。
枚举的第一行并不是简单的列出实例。第一行实际上要创建、并列出枚举类的所有实例。 因此,需要根据构造器是形参而列表传入对应的参数。

抽象枚举:day-06 Gender.java
枚举可以是抽象的。只要你为枚举定义了抽象方法,系统会自动为该枚举添加abstract修饰。
如果枚举是抽象的,那就需要在第一行用匿名内部类语法去创建枚举实例。

枚举实现接口:
枚举可以实现接口:
A、枚举实现接口,必须实现接口中所有抽象方法,这就是一个普通枚举。
B、枚举实现接口,但并不实现接口中所有的抽象方法,此时,就变成一个抽象枚举——需要在第一行用匿名内部类语法去创建枚举实例。
用匿名内部类实现后,编译生成了枚举名.class 枚举名$1.class 枚举名$2.class…等文件,证明枚举第一行用内部类列出的实例实际上是匿名子类的实例,而不是枚举类的实例。用这些实例调用同一重写的抽象方法,将表现出不同的行为方式。

Java修饰符的适用范围:
传统:private |protected|public static final sbstract
多线程:synchronized、volatile
序列化:transient
Native:修饰方法、用于适用C/C++调用底层平台的API来实现该方法。
Strictfp:严格浮点数,可修饰类、接口、枚举、方法。用于保证java会严格按照浮点数的规范来计算。从而不会引起浮点数的精度问题。

Jar命令:
对于实际的java项目部署时,通常都会打包成“压缩文件(归档文件)”。
Java的多个class文件,可以用winrar压缩,但这种压缩包不是java本身能识别的压缩包,因此必须先解压,然后再被java使用。
Java本身提供了jar命令来生成压缩包(归档文件)。这种压缩包文件就无需解压,java命令本身可以直接加载这种压缩包中class文件。
在java领域中,通常有如下三种压缩包(格式完全一样,只是后缀不同)
1、jar包:就是很多class文件的压缩包。最常见的一种
2、war包:就是一个完整web应用的压缩包。当需要部署该web应用时,只要把war包部署到服务器即可。
3、Ear包:它通常是一个企业级项目的压缩包,它通常会包括EJB的jar包和web部分的war包。

jar命令位于JDK的bin目录下。与javac、java、javadoc、javap等命令位于同一个目录
在jar包根目录下,会自动生成一个META-INF子目录,该子目录下存放了一个清单文件:MANIFEST.MF
jar命令的选项:
-c, --create 创建压缩包
-t, --list 查看压缩包 ——直接用winrar更方便
-x, --extract 从档案中提取(解压)指定的 (或全部)文件——直接用winrar更方便
-u, --update 更新现有 jar 档案
-m, --manifest=FILE 使用自定义的清单文件(MANIFEST.MF) 意思是提取自定义的清 单内容添加到MANIFEST.MF文件中
-M, --no-manifest 不生成清单文件
F:为压缩包指定名字

传入的值和选项的顺序必须一致。
创建压缩包:
Jar -cf 压缩包文件名 . 创建名为test.jar的压缩包,*.*为所有文件,即指定要压缩的文件
Jar -cvf 压缩包文件名 . v:显示压缩的过程 v(verbose)

创建压缩包不添加清单文件
Jar -cMf 压缩包文件名 .
Jar -cvMf 压缩包文件名 . 显示压缩的过程

创建压缩包使用自定义清单文件:
Jar -cmf 清单文件名 压缩包文件名 .
Jar -cvmf 清单文件名 压缩包文件名 . 显示压缩的过程

查看压缩包
Jar tf 压缩包文件名
Jar tvf 压缩包文件名 显示压缩过程

解压压缩包
Jar xf 压缩包文件名
Jar xvf 压缩包文件名 显示过程

更新
Jar uf 压缩文件名 要更新的文件
Jar uvf 压缩文件名 要更新的文件名 显示过程
如果要更新的文件没有在压缩包内,则会将文件添加到压缩包,如果要添加的文件已经在压缩包内,则会对文件进行更新,
可执行性的jar包
e:指定该jar包中哪个类是“主类”,主类就是包含public static void main(String[] args)方法的类。——该方法将作为程序的入口。
Java命令本身可以加载jar包中的class文件,但是jar通常包含了很多个class文件,因此需要指定哪个类是主类。即压缩jar包时,加-e选项,如:
Jar -cvef 主类的完整类名 压缩包文件名 需要压缩的文件包 主类完整类名带包名
Jar -cvef 压缩包文件名 主类的完整类名 需要压缩的文件包

Java -jar 压缩文件名 用于直接执行jar包

多版本的jar包
使用jar包的原因:当程序既想利用新版本java的技术优势,又想兼容老版本的JDK,此时就可使用多版本的jar包。
做法是:为同一个程序,提供针对不同JDK版本的java源文件,对应生成不同版本的class文件,通过多版本的JAR包将这些class文件都打包在一个jar包中。如:
Jar cfe test.jar lee.Test -C java7/ lee --release 11 -C java11/ lee
java命令会自动选择它所兼容的class版本
Javac --release 7 -d . *.java --release 用于指定使用特定版本的java来编译源文件
-C:为不同版本的class指定对应的目录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值