Java版本
Java SE :Java standard edition
Java EE : Java Enterprise(企业) Edition
OOP: Object Oriented Programming
DAO: Database Access Object
Java开发工具
JDK(java development kit) java开发工具包
JRE(java se runtime environment) java 运行时环境
JVM (java visual machine) Java虚拟机
包含关系
JDK包含JRE,JRE包含JVM
Java API 文档
应用程序接口,java类库
标识符
标识符: Java 对各种变量、方法和类等要素命名时使用的字符序列称为标识符
凡是自己可以起名字的地方都叫标识符。
String 类型变量
引用类型变量
进制
二进制:Ob或OB开头
八进制:O开头
十六进制:Ox或OX开头
运算符
short s = 10;
//s = s + 2; //编译失败,2默认是int类型的,赋值给short类型编译通不过
s += 2; //是可以的,+=会强制自动类型转换
System.out.println(s);
所以推荐第二种方式(s += 2;)的方式实现
逻辑运算符
针对的是布尔类型的运算
三元运算符
凡是可以使用三元运算符的地方都可以改写成if-else语句
注意:运算符的第二个运算值和第三个运算值需要是同一类型的,否则编译无法通过(例如一个是int一个是double则运算时会先进行自动类型提升再运算;若两个操作数不可转换则返回Object类型)
? :
int n = 1;
int m = 2;
(m > n) ? m : n; //比较mn,m大于n若为真,结果是第二个运算值即m,若为假,结果是n
Scanner
/**
* 使用Scnanner类实现从键盘获取值
* 步骤:
* 1、创建Sacnner实例----Scanner scanner = new Scanner(System.in);
* 2、调用Scanner类的相关方法获取指定类型的变量----scanner.nextInt()
*/
break 和 continue 关键字
break ---- 可以用于switch分支结构和循环结构中, 用于完全结束一个循环,跳出循环体break还可以使用标签直接跳出外层循环,(如使用outer标签),此时break后跟着所使用的标签
continue ------- 只可以用于循环结构中, 用于结束当次循环,接着开始下次循环
数组
一维数组的初始化和遍历
//数组的动态初始化
String[] names = new String[6];
//数组的静态初始化
int[] ids = new int[]{1,2,3,4,5};
//遍历数组
for (String name : names){
System.out.println(name);
}
//boolean类型默认初始值是false (底层true是1,false是0)
//char类型的默认初始化值是0 注意:不是'0'
//引用类型的初始化值是null
二维数组的初始化和遍历
int[][] arr1 = new int[][]{{1,2,3}, {3,4}, {7,6,6,7}};
//遍历二维数组
for(int i = 0; i < arr1.length; i++){
for(int j = 0; j < arr1[i].length; j++){
System.out.print(arr1[i][j]);
}
}
//for-each遍历二维数组
for(int[] arr : arr1){
for(int arrin : arr){
System.out.print(arrin);
}
}
为数组初始化随机两位数
//随机整数要求:两位数 [a,b] 公式: (Math.random() * (b - a + 1) + a) double类型的两个数之间的数;
for (int i = 0; i < arr.length; i++){
arr[i] = (int)(Math.random() * (99 - 10 + 1) + 10);
}
十大内部排序算法
选择排序: 直接选择排序、 堆排序
交换排序: 冒泡排序(Bubble Sort)、 快速排序(Quick Sort)
插入排序: 直接插入排序、 折半插入排序、 Shell排序
归并排序
桶式排序
基数排序
int[] arr = new int[]{34,44,65,23,78,45,65,52,90,19,-12};
//冒泡排序
for(int i = 0; i < arr.length - 1; i++){
for(int j = 0; j < arr.length - 1 - i; j++){
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
Arrays工具类
int[] array2 = new int[]{-12, -3, 4, 34, 56, 67, 89, 109, 333, 666};
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
//boolean equals()
boolean result = Arrays.equals(arr1, arr2);
System.out.println(result);
//String toString() : 输出数组信息
System.out.println(Arrays.toString(array2));
//void fill() : 将指定的值替换数组中的每一个值
Arrays.fill(arr1, 90);
System.out.println(Arrays.toString(arr1));
//void sort() : 排序从小到大(内部实现使用的DualPivotQuicksort【双轴快速排序】)
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
//int binarySearch() : 二分查找
int index = Arrays.binarySearch(array2, 666);
System.out.println(index);
权限修饰符(实现了良好的封装)
类的权限修饰符: public, 或省略
成员变量的权限修饰符: public, protected, private, 或省略
方法的权限修饰符: public, protected, private
访问控制符:
-
private: 当前类访问权限;只能在当前类内部被访问
-
缺省: 包访问权限;可以被相同包下的其他类访问
-
protected:子类访问权限;可以被不同包下的子类访问,通常用它修饰是希望子类重写此方法
-
public:公共访问权限,可以被所有类访问,不论是否在统一包下
访问控制级别:
private < default(缺省) < protected < public
使用访问控制符的几个原则:
- 类中绝大部分成员变量都应该使用private修饰,只有一些用static修饰,类似全局变量的成员变量才考虑使用public修饰,
package,import
import不是必须的,只要坚持在类里使用其他类的全名,则可以无需使用import语句(不用莫非我是傻子…)
在有些情况下,不得不使用全名,比如;
在java.util 包 和 java.sql 包中都含有Date类,在引入两个包的情况下,若要使用Date类的实例只能使用全称:
java.sql.Date d = new java.sql.Date();
对象数组的内存解析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iUaUaANJ-1625637925820)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210328094409690.png)]
ps: 引用类型的变量只可能存储两类值: null, 或 地址值(包含变量的类型)
方法的参数传递机制(值传递)
值传递(参数复制一份副本)
无论是基础类型还是引用类型均是值传递
只不过,引用类型传递时,引用类型实参把它的地址值赋给形参
注意:String类型的变量由于具有不可变性,所以虽然String也是引用类型,但是在进行值传递时String对象中的字符序列是不可变的
可变个数的形参:
在最后一个形参的类型后增加三点(…)则表明该形参可以接受多个参数值,多个参数值被当作数组传入
ps:个数可变的形参只能处于形参列表的最后也就是一个方法中最多只能有一个形参可变的参数,个数可变的形参本质就是一个数组类型的形参
**值传递(**pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
**引用传递(**pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
递归
递归方法(在方法中调用它自身)
//斐波那契数列: 一个数等于前两个数的和
public int fibonacc(int n){
if(n == 1 || n == 2){
return 1;
}else{
return fibonacc(n - 1) + fibonacc(n - 2);
}
}
成员变量和局部变量
变量
- 成员变量:
- 实例变量
- 类变量(static修饰)
- 局部变量:
- 形参
- 方法局部变量
- 代码块局部变量
- 系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化
成员变量无须显示的初始化,局部变量除形参外必须显示的初始化
java中允许局部变量和成员变量重名,在方法中若要访问被覆盖的成员变量,可以使用this(对于实例变量)或 类名(对于类变量)作为主调访问
构造器
因为构造器主要用于被其他方法调用,用来返回该类的实例,通常把构造器设成public访问权限
默认的构造器修饰符和类的修饰符相同
JavaBean
JavaBean是一种Java语言写成的可重用组件
所谓JavaBean,是指符合如下标准的Java类:
- 类是公共的(public)
- 有一个无参公共构造器
- 有属性且有对应的get,set方法
UML类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CxOE68VZ-1625637925824)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210328154028954.png)]
MVC设计模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mmjVITJp-1625637925825)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210329125116517.png)]
继承(extends)
子类一旦继承父类后,子类中就获取了父类中声明的所有属性和方法(包括父类中那个private修饰的属性和方法)
所有的类都直接或间接继承自java.lang.Object类
方法重写(overrdie or overwrite)(覆盖)
方法重写的要求:
- 子类重写的方法必须和父类被重写的方法方法名称、参数列表都相同(覆盖父类方法)
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 父类中方法的返回值类型void,则子类重写的方法返回值类型只能是void
- 父类中方法的返回值是A类型,则子类重写的放法的返回值类型可以是A类及A类的子类
- 父类中方法的返回值是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
- 子类不能重写父类中声明为private权限的方法
- 子类方法抛出的异常不能大于父类被重写方法的异常
方法重载(OverLoad)
- 重载(Overloading)实在一个类里面方法名字相同,但是方法参数列表(类型,个数,顺序有一个不同就可以)不同,返回值类型可以不同,也可以相同
Super关键字
父类的…
this(当前类、方法、对象的…)
1、我们可以在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显示的调用父类中的属性或方法
比如当子类和父类定义了同名的属性,在子类中调用父类的属性则可以使用super关键字调用
2、super调用构造器
-
我们可以在子类的构造器中使用 “super(形参列表)” 的方式调用父类中指定的构造器
-
“super(形参列表)” 的使用必须在子类构造器的首行
-
在子类的构造器中,对于"this(形参列表)" 和 “super(形参列表)” 只能用一个(因为都要放到首行)
-
子类构造器首行本来也会默认调用父类的构造器:"super();"
-
1、为什么在实例化子类的对象时,会先调用父类的构造器?
答:子类继承父类后,获取到父类的属性和方法,这些属性和方法在使用前必须先初始化,所以须先调用父类的构造器进行初始化
2、在哪里调用父类的构造器?
答:在子类构造器的第一行会隐式的调用 super();,即调用父类的构造器
如果父类中没有定义空参的构造器,则必须在子类的构造器的第一行显示的调用super(参数); ,以调用父类中构造器
如果子类中构造器的第一行写了this();,则隐式的super();就会消失,因为super()和this()都只能在构造器的第一行定义
ps:当通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器(因为一子类中的构造器总有一个构造器中有super(); 而没有的都是this(); 而this();调下去总会调用到含有super();的构造器),进而调用父类的父类的构造器直到调用java.lang.Object类中的空参构造器
多态性
class Animal{
public int age;
public void eat(){
System.out.println("Animal的eat方法");
}
}
class Dog extends Animal{
public String age;
public void run(){
System.out.println("run方法");
}
public void eat(){
System.out.println("这是Dog的eat方法");
}
}
public class Polym {
public static void main(String[] args) {
Animal dog = new Dog();
//下面一行代码会输出“这是Dog的eat方法”
dog.eat();
//dog.run();本行代码会报错,因为Animal中没有run();方法
}
}
dog引用变量的编译时类型是Animal,而运行时类型是Dog,
当运行时dog变量调用Dog类的eat()方法,而不是父类的eat()方法,
相同类型的不同变量调用同一个方法时(子类重写父类的方法)呈现不同的行为特征这就是多态(举个栗子: Dog 和 Cat 继承自 Animal,并且Dog 和 Cat 重写了Animal的eat()方法,则Animal 类型的 Dog 和 Cat 调用 eat()方法时,分别运行的是各自重写的eat()方法 )
引用类型在编译阶段只能调用编译时类型所具有的方法,但运行时则执行它运行时类型的方法,因此,引用变量只能调用声明该变量时所用类的方法;例如:
Object p = new Person();
其中变量p只能调用Object类中的方法,而不能调用Person类中的方法(除非Person类重写了Object类的方法)
ps: 多态性只适用于方法,不适用于属性(属性都是编译时类型)
类型转换(强制类型转换)
Student extends Person
Person p = new Student();
Student p1 = (Student) p;//在这种情况下,p是可以强制类型转换成 Student类型的变量,因为p本身是一个Student类型的变量
Person p2 = new Person();
Student p3 = (Student) p2;//这是错误的,Person类型的变量p2不可以强制转换为Student,因为p2本身就是一个Person对象
instanceof关键字
a instanceof A : 判断对象a是否是A的实例,如果是,返回true,如果不是,返回false
使用情景: 未了避免在向下转型时出现ClassCastException的异常,在向下转型时,可以先进行instanceof的判断,一旦返回true就向下转型,如果返回false就不向下转型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gteeursz-1625637925828)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210401103926471.png)]
== 和 equals()区别
== :运算符
1、可以使用在基本数据类型和引用数据类型
2、如果比较两个基本数据类型的变量:比较两个变量保存的数据是否相等(类型不一定相同),如果比较的时引用类型的变量:比较两个变量的地址值是否相等(即两个变量知否指向同一个对象实例)
equals(): 是方法
1、只适用于引用数据类型(因为他是一个方法)
2、Object类中equals()的定义是和 == 相同的
public boolean equals(Object obj) {
return (this == obj);
}
(ps:上面的方法,this是调用该方法的对象,obj是赋值形参的对象)
3、像String、Date、File、包装类等都重写了Object类的equals()方法,重写后比较的是两个对象的 “实体内容”(属性) 是否相同
4、通常情况下我们自定义的类如果使用equals()的话,一般也是比较属性是否相同,那么就要对Object类的equals()方法重写
ToString()方法
像String,Date,File,包装类等都重写了Object类的toString()方法,使得在调用toString 时返回”实体内容“
Junit单元测试
@Test注解
包装类(Wrapper Class)
基本数据类型的包装类:
char ------Character
int ------Integer
除这两个外其他六个基本类型的包装类都是首字母大写
JDK1.5以后Java提供了自动装箱(Autoboxing)和自动拆箱(AutoUnboxing)功能
自动装箱就是可以把一个基本数据类型的变量直接赋给包装类型的变量
int num1 = 20;
Integer in1 = num1;
自动拆箱就是可以把一个包装类型的变量直接赋给对应的基本类型变量
int num3 = in1;
此外,包装类还可以实现字符串和基本数据类型之间的转换,把字符串类型的值转换为基本数据类型有两种方式:
//1、利用包装类提供的parseXxx(String s)静态方法(除Character之外所有包装类均提供了该方法)
int i = Integer.parseInt("123");
//2、利用包装类提供的valueOf(String s) 静态方法
int i1 = Integer.valueOf("123");
在实际使用中,Integer类中包含了此方法,所以可以直接把字符串类型赋值给Integer类型
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
Integer i2 = new Integer("123");
**包装类转换为基本数据类型:**xxxValue()
Integer i4 = new Integer(123);
int i3 = i4.intValue();
String类也提供了多个重载的valueOf方法用于将基本数据类型转换成字符串如:
String s = String.valueOf(2.35f);
还用一种便捷方式:String s = 2.35f + “”;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m2w2to1A-1625637925830)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210404142544976.png)]
-
包装类中的方法:
-
包装类中都含有 compareTo(Double anotherDouble)
public int compareTo(Double anotherDouble) { return Double.compare(value, anotherDouble.value); }
a.compareTo(b); 如果返回值0,表示相等。如果返回值为1,说明a>b,如果返回值为-1,说明a<b
-
static compare(double d1,double d2)
比较两个基本类型的大小,如果返回值0,表示相等。如果返回值为1,说明d1>d2,如果返回值为-1,说明d1<d2
-
for(;;)相当于while(true)
二者跳循环都得使用break;语句
//1、实例化Scanner获取成绩
Scanner scanner = new Scanner(System.in);
//2、实例化Vector对象
Vector v = new Vector();
//3、for循环给Vector添加元素
int maxScore = 0;
for(;;){
System.out.println("请输入成绩(以负数代表结束):");
int score = scanner.nextInt();
if(score < 0){
break;
}
if(score > 100){
System.out.println("请重新输入:");
continue;
}
v.addElement(score);//自动装箱
//4、获取成绩最大值
if(maxScore < score){
maxScore = score;
}
}
//5、遍历Vector,得到每个成绩和最大值比较,进行判定级别
for(int i = 0; i < v.size(); i++){
if(maxScore - (int)v.elementAt(i) <= 10){
System.out.println("成绩为:" + (int)v.elementAt(i) + "\tA等");
}
else if( maxScore - (int)v.elementAt(i) <= 20){
System.out.println("成绩为:" + (int)v.elementAt(i) + "\tB等");
}
else if( maxScore - (int)v.elementAt(i) <= 30){
System.out.println("成绩为:" + (int)v.elementAt(i) + "\tC等");
}else {
System.out.println("成绩为:" + (int)v.elementAt(i) + "\tD等");
}
}
static关键字
静态成员(方法,变量)不允许访问非静态成员(方法,变量)
static修饰的变量是属于类的
static可以修饰 属性、方法、代码块、内部类
static修饰的属性:静态属性: 我们创建多个对象,多个对象共享一个静态属性。当通过某一个对象修改静态属性,会导致其他对象再调用此属性时,是修改过的
静态变量、方法随着类的加载而加载,而实例变量在new的时候才创建,由于类只会加载一次,所以静态变量在内存中也只会存在一份(存放在方法区的静态域中)
静态属性举例:System.out , Math.PI,
静态方法中只能调用静态的方法和属性(因为静态方法随着类加载而加载,类只加载一次,所以静态方法也只加载一次,所以静态方法中不能调用非静态方法)
非静态方法中既可以调用静态方法也可以调用非静态方法
静态方法中不能使用this关键字、super关键字(this表示当前对象的,)
static关键字的作用是把类的成员变成类相关而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象
单例设计模式(Singleton)
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。
单例(Singleton)设计模式-应用场景
网站的计数器,一般也是单例模式实现,否则难以同步。
应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志 文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库 资源。
项目中,**读取配置文件的类,**一般也只有一个对象。没有必要每次使用配置 文件数据,都生成一个对象去读取。
Application 也是单例的典型应用
Windows的**Task Manager (任务管理器)**就是很典型的单例模式
Windows的**Recycle Bin (回收站)**也是典型的单例应用。在整个系统运行过程 中,回收站一直维护着仅有的一个实例
代码块(初始化块)
final 关键字
final关键字可以修饰类、变量和方法
final类:表明这个类不能被继承,final类中的所有成员方法都会被隐式的指定为final方法
final成员变量: final修饰的成员变量一旦有了初始值,就不能被重新赋值:所以java规定final修饰的成员变量必须由程序员显式的指定初始值;final修饰的类变量(static)必须在静态初始化块中指定初始值或声明该变量时指定初始值;final修饰的实例变量,要么在定义该实例变量时指定初始值,要么在普通初始化块或构造器中指定初始值
final修饰引用类型变量:引用类型变量保存的仅仅是一个引用,final只保证这个引用类型变量所引用过的地址不会改变,但对象本身可以发生改变
可执行"宏替换(宏定义)"的final变量:对于一个final变量,只要该变量满足三个条件:
final修饰符修饰
定义final变量时指定了初始值
该初始值可以在编译时就被确定下来
public class FinalVariableTest {
public static void main(String[] args) {
//定义一个普通局部变量
final int a = 5;
System.out.println(a);
}
}
//此程序中,变量a实际根本不存在,当程序执行System.out.println(a);时,实际转换为执行System.out.println(5);
final修饰符的一个重要用途就是定义宏变量,满足以上三个条件的final变量实际就是一个宏变量,编译器会把程序中所有用到该变量的地方直接替换成该变量的值
ps:
java会使用常量池来管理曾经用过的字符串直接量,例如:
String b = "java"; String c = "java"; System.out.println( b == c);//返回true //java在执行String b = "java";之后常量池中就会缓存一个字符串"java",如程序再执行String c = "java";系统会让c直接指向常量池中的"java"字符串,所以返回true
**final方法:**final修饰的方法不可以被重写
final类: final修饰的类不可以有子类
抽象类与抽象方法
抽象类:
- 此类不能实例化(但是还提供构造器以给子类使用)
- 开发中都会提供抽象类的子类,让子类实例化对象,实现相关操作
抽象方法:
- 抽象方法只有方法的声明,不能有方法体
- 包含抽象方法的类一定是抽象类
- 子类必须重写父类(包括间接父类)的抽象方法
**抽象类的作用:**抽象类体现的是一种模板模式的设计思想,抽象类作为多个子类的设计模板,子类在抽象类的基础上进行扩展、改造。(抽象类是从多个类抽象出来的模板)
设计模式:模板方法(TemplateMethod)
接口(Interface)
-
定义: 接口定义了某一批类所需要遵守的规范,接口不提供任何实现,接口体现的是规范和实现分离的设计思想
-
接口中的成员变量只能是静态常量(public static final),接口中没有构造器和初始化块,所以成员变量只能在定义初始化
-
java9增加了带方法体的私有(private)方法,这也是java8埋下的伏笔:Java8允许在接口中定义带方法体的默认(defalut)方法和类(static)方法——这样势必会引发一个问题,当两个默认方法(或类方法)中包含一段相同的实现逻辑时,程序必然考虑将这段逻辑抽取为工具方法,而工具方法是应该被隐藏的,这就是java9增加私有方法的必然性
/**
* 接口:
* 1、接口和类是并列的结构
*
* 2、如何定义接口:
*
* 2.1 jdk7及以前:只能定义全局常量和抽象方法
* >全局常量:public static final 修饰的,(书写时可以省略)
* >抽象方法:public abstract 修饰的 (书写时也可以省略)
*
* 2.2 jdk8及以后:除了全局常量和抽象方法外,还可以定义静态方法、默认方法 (defalut)
* >接口中定义的static方法只能通过接口来调用
* >通过实现类的对象可以调用接口中的default方法
*
*
* 3、接口中不能定义构造器,意味着接口不可以实例化
*
* 4、接口通过让类实现(implements)的方式来使用
* 如果实现类重写了接口中的所有抽象方法,则此实现类就可以实例化
* 如果实现类没有重写接口中所有的抽象方法,则此实现类仍为一个抽象类
*
* 5、java类可以实现多个接口
* 如: class A extends B implements C,D,E
*
* 6、接口与接口之间可以继承,而且可以多继承,接口只能继承接口不能继承类
*
*/
只有一个抽象方法的接口被称为函数式接口
函数式接口
函数式接口的几点特征:
- 函数式接口只有一个抽象方法
- default方法某默认实现,不属于抽象方法
- 接口重写了Object的公共方法也不算入内
面向接口编程
代理模式(Proxy)
内部类
内部类的作用:
- 内部类实现了更好的封装,可以把内部类隐藏在外部类之内
- 内部类成员可以直接访问外部类的私有数据,但外部类不能访问内部类的实现细节
- 匿名内部类适合用于创建仅需要使用一次的类
匿名内部类:
由于匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口中的所有抽象方法
关于匿名内部类的两条规则:
- 匿名内部类不能是抽象类。因为系统再创建匿名内部类时,会立即创建匿名内部类的对象
- 匿名内部类中不能显式定义构造器。(匿名内部类只有一个隐式的无参构造器,但如果通过继承父类的方式创建匿名内部类时,也继承了父类的构造器)由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类中可以定义初始化块,可以通过实例初始化块来完成构造器需要完成的事情
/**
* 内部类
* 1、内部类的分类:成员内部类(静态的,非静态的) vs 局部内部类和匿名内部类(方法内,代码块内, 构造器内)
*
* 2、成员内部类:
* >作为一个类,类内可以定义属性、方法、构造器...;
* 可以被final修饰(不可以被继承);可以被abstract修饰
*
* >作为外部类的一个成员,可以调用外部类的成员;
* 可以用static修饰; 可以被四种权限修饰符修饰
*
* 3、关注如下三个问题
* 3.1如何实例化成员内部类的对象
* 3.2如何在成员内部类中区分调用外部类的结构
* 3.3开发中局部内部类的使用
*/
public class InnerClassTest {
public static void main(String[] args) {
//3.1如何实例化成员内部类的对象
//创建静态内部类的实例
Animal.Dog dog = new Animal.Dog();
//创建非静态内部类的实例(由于非静态内部成员属于类的实例,所以需要先创建实例再 new )
Animal a = new Animal();
Animal.Cat cat = a.new Cat();
}
}
class Animal{
String name;
int age;
static class Dog{
public Dog(){
}
public void sing(){
System.out.println("卡拉是条狗");
}
}
class Cat{
String name;
public void sing(){
System.out.println("kk是条猫");
//Animal.this.eat(); 调用外部类的非静态属性,eat()前面省略的是 Animal.this
eat();
}
// 3.2如何在成员内部类中区分调用外部类的结构
public void display(String name){
System.out.println(this.name);//cat中的name
System.out.println(Animal.this.name);//外部类的name
}
}
public void eat(){
System.out.println("eat method");
}
}
// 3.3开发中局部内部类的使用
//返回一个实现了Comparable接口的对象
public Comparable getComparale(){
//创建一个实现了Comparable接口的类:局部内部类
//方式1:
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
return 0;
}
}
return new MyComparable();
//方式2:(匿名内部类的匿名对象)
return new Comparable() {
@Override
public int compareTo(Object o) {
return 0;
}
}
}
异常(java.lang.Throwable)
包括 error 和 exception
**java.lang.Error:**Java虚拟机都无法解决的严重问题
举个栗子:
- 栈溢出:java.lang.StackOverflowError
- 堆溢出:java.lang.OutOfMemoryError
java.lang.Exception: 一般性问题,如:空指针访问,试图读取不存在的文件,网络链接中断,数组角标越界
又分为编译时异常和运行时异常:
异常的处理方式:
java采用的异常处理机制,是将可能出现异常的代码集中起来
-
方式一:try - catch - finally (相当于直接干掉异常)
-
方式二:throws + 异常类型 (往上抛异常,自己干不掉让上面的人干)
异常的处理:抓抛模型
过程一: “抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对 象,并将此对象抛出;一旦抛出异常对象后,其后的代码不再执行
关于异常的产生有两种方式:①:系统自动生成的异常对象
②:手动生成一个异常对象,并抛出 (使用throw)
过程二:“抓”:可以理解为异常的处理方式 ①:try - catch - finally; ②throws
try - catch - finally :
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//异常处理代码
}catch(异常类型2 变量名2){
//异常处理代码
}
....//可以有多个catch
finally{
//一定会执行的代码
}
//catch中常用的方法:
// String getMessage();
// void printStackTrace();
//在try结构中声明的变量在出了try结构就不可以调用了
ps:finally中声明的代码是一定会执行的代码,即使catch中又出现了异常了,即使try中有return语句,catch中有return语句等;
像数据库连接,输入输出流、网络编程Socket等资源,jvm是不能自动回收的,我们需要手动进行资源释放,此时的资源释放就需要声明在finally中
开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch了
**throws + 异常类型:**手动的抛出一个异常,在方法内部
写在方法的声明处,指明此方法执行时有可能会抛出异常
throw异常
用户自定义异常类:
1、需要自定义的异常类继承现有的异常类【Exception(编译时异常) RunTimeException(运行时异常)】
2、提供全局常量 serialVersionUID 【static final long serialVersionUID-7034897190745766939L;】
多个if和if-else的区别
int a=1;
if(a==1){System.out.println("1");}
if(a==2){System.out.println("2");}
if(a==3){System.out.println("3");}
if(a==1){System.out.println("1");}
else if(a==2){System.out.println("2");}
else if(a==3){System.out.println("3");}
else if(a==4){System.out.println("4");}
//多个if 不管你前面ture与否后面的都执行
//else-if 如果前面的有一个成立 那么后面的都不执行
Lambda表达式
-
lambda表达式支持将代码块作为方法参数,(所以就可以直接在参数处直接创建只有一个抽象方法的接口的实例)【只有一个抽象方法的接口被称为函数式接口】
-
lambda表达式的主要作用就是代替匿名内部类的繁琐语法。(直接写需要匿名内部类中需要重写的方法的形参列表和方法体,省略new 匿名内部类名{})
-
lambda的形式:
- 形参列表。【允许省略形参类型,如果只有一个参数,甚至允许省略()】
- 箭头 【 -> 】
- 代码块。【如果只包含一条语句,允许省略 { } ,如果只有一条return语句,甚至可以省略return 关键字】
-
函数式接口代表只包含一个抽象方法的接口,但可以包含多个默认方法(default),类方法(static)
-
Lambda表达式的类型,被称为目标类型(target type)Lambda表达式的目标类型必须是函数式接口(@FunctionalInterface)
采用匿名内部类语法来创建函数式接口的实例,只需要实现一个抽象方法,在这种情况下就可以采用Lambda表达式来创建对象
由于Lambda表达式的结果就是被当成对象,因此程序中完全可以使用Lambda表达式进行赋值
//Runnable接口是一个函数式接口,只包含一个无参的方法 //Lambda表达式代表的匿名方法实现了Runnable接口中唯一的抽象方法 //因此下面的Lambda表达式创建了一个Runnable对象 Runnable r = () -> { for(int i = 0; i < 100 ; i++){ System.out.println("这是Runnable实例中的无参方法体"); } };
-
如果Lambda表达式的代码块只有一条语句,还可以在代码块中使用方法引用和构造器引用
-
实际上,Lambda表达式时匿名内部类的一种简化
枚举类
当需要定义一组常量时,强烈建议使用枚举类
-
某些情况下,一个类的对象是有限且固定的,就可以使用枚举类
为什么不使用静态常量来表示枚举?
存在如下问题: ①:类型不安全,②:没有命名空间,③打印输出意义不明确
-
Java5新增了一个 enum 关键字(它与class 、 interface 关键字的地位相同)用来定义枚举类,枚举类是一种特殊的类
-
枚举类和普通类的区别
-
枚举类默认继承自 java.lang.Enum 类,而不是默认继承 Object 类,其中Enum类实现了java.lang.Seriaizable 和 java.lang.Comparable 接口
-
使用 enum 定义枚举类默认会使用 final 修饰(抽象枚举类除外)
-
枚举类的构造器默认使用 private 访问控制符修饰,也只能使用 private 修饰
-
枚举类的所有实例必须在枚举类的第一行显式的列出,不需要使用new关键字(不需要显式的调用构造器)否则这个枚举类永远都不能产生实例,列出这些实例时,系统会自动添加 public staic final 修饰,枚举类的构造器只能是私有的(枚举类是final的)
-
-
枚举类提供了一个 values() 方法,该方法可以方便的遍历所有枚举值(返回一个数组类型的,再遍历数组就可以打印出所有的枚举值)
-
valueOf() 方法:
-
枚举类的实例只能是枚举值,而不是随意的通过 new 来创建枚举类对象
-
JDK1.5增加枚举类后对 switch 进行了扩展: switch 的控制表达式可以是任何枚举类型,并且当 switch 的控制表达式使用枚举类型时,后面 case 表达式中的值可以直接使用枚举值的名字,无须使用 枚举类.枚举值 (也就是无需添加枚举类作为限定了)
-
一旦为枚举类显式的定义了带参数的构造器,在枚举类中列出枚举值时就需要显式地传入参数
-
实现接口的枚举类
-
抽象枚举类
对象与垃圾回收
gc: garbage collector(垃圾回收器)
-
垃圾回收机制的特征:
- 垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源(例如数据库连接、网络IO等资源)
- 程序无法精确控制垃圾回收的进行,垃圾回收会在何时的时候进行。当对象永久性失去引用后,系统就会在合适的时候回收它所占的内存
- 在垃圾回收机制回收任何对象之前,总会先调用它的 finalize() 方法,该方法可能使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消回收
-
强制垃圾回收
这种强制只是通知系统进行垃圾回收,但系统是否进行垃圾回收依然不确定
强制系统垃圾回收的两种方式:
①:调用 System 类的 gc() 静态方法 System.gc();
②:调用 Runtime 对象的 gc() 实例方法:Runtime.getRuntime().gc();
-
finalize 方法
在垃圾回收机制回收某个对象所占用的内存前,需要程序调用适当的方法清理资源,如果没有明确指定清理资源,java默认使用 finalize() 方法(该方法是定义在Object 类中的实例方法)
任何Java类都可以重写 finalze() 方法
finalize() 方法具有如下特点:
①:永远不要主动调用某个对象的 finalize() 方法,让垃圾回收机制调用
②:finalize() 方法的调用具有不确定性,不要把finalize() 方法当成一定会执行的方法
③:当JVM 执行可恢复对象的 finalize() 方法时,可能使该对象或系统中的其他对象重新变成可达状态
对象的引用
-
Java 对对象的引用方式有四种:
①:强引用(StrongReference)
最常见的引用方式,创建一个对象,并把它赋给一个引用变量
②:软引用(SoftReference)
软引用需要通过SoftReference 类来实现(当一个对象只有软引用时,有可能被垃圾回收机制回收,当内存充足时,不会被系统回收,当内存不足时,可能会回收它)
③:弱引用(WeakReference)
弱引用通过WeakReference 类来实现,当系统垃圾回收机制运行时,不管系统内存是否充足,总会回收该对象作占用的内存
④:虚引用(PhantomReference)
通过 PhantomReference 类来实现,类似于完全没有引用,主要用于跟踪对象被垃圾回收的状态
Jar包
-
Jar : Java Archive File Java档案文件,通常 JAR 文件是一种压缩文件,与常见的 zip 文件兼容。JAR包与 ZIP 文件的区别就是在 JAR 包中默认包含了一个名为 META-INF/MANIFEST.MF 的清单文件
-
jar 命令
- 创建 JAR 文件:jar cf test.jar -C dist/. 【cf :creat file】
- 创建 JAR 文件,并显式压缩过程 :jar cvf test.jar
- 不使用清单文件:jar cvfM test.jar
- 自定义清单文件内容:jar cvfm test.jar manifest.mf
- 查看 jar 包内容:jar tf test.jar
- 查看 jar 包详细内容:jar tvf test.jar
- 解压缩:jar xf test.jar
- 带提示信息解压缩:jar xvf test.jar
注解(Annotation)
定
强制系统垃圾回收的两种方式:
①:调用 System 类的 gc() 静态方法 System.gc();
②:调用 Runtime 对象的 gc() 实例方法:Runtime.getRuntime().gc();
-
finalize 方法
在垃圾回收机制回收某个对象所占用的内存前,需要程序调用适当的方法清理资源,如果没有明确指定清理资源,java默认使用 finalize() 方法(该方法是定义在Object 类中的实例方法)
任何Java类都可以重写 finalze() 方法
finalize() 方法具有如下特点:
①:永远不要主动调用某个对象的 finalize() 方法,让垃圾回收机制调用
②:finalize() 方法的调用具有不确定性,不要把finalize() 方法当成一定会执行的方法
③:当JVM 执行可恢复对象的 finalize() 方法时,可能使该对象或系统中的其他对象重新变成可达状态
对象的引用
-
Java 对对象的引用方式有四种:
①:强引用(StrongReference)
最常见的引用方式,创建一个对象,并把它赋给一个引用变量
②:软引用(SoftReference)
软引用需要通过SoftReference 类来实现(当一个对象只有软引用时,有可能被垃圾回收机制回收,当内存充足时,不会被系统回收,当内存不足时,可能会回收它)
③:弱引用(WeakReference)
弱引用通过WeakReference 类来实现,当系统垃圾回收机制运行时,不管系统内存是否充足,总会回收该对象作占用的内存
④:虚引用(PhantomReference)
通过 PhantomReference 类来实现,类似于完全没有引用,主要用于跟踪对象被垃圾回收的状态
Jar包
-
Jar : Java Archive File Java档案文件,通常 JAR 文件是一种压缩文件,与常见的 zip 文件兼容。JAR包与 ZIP 文件的区别就是在 JAR 包中默认包含了一个名为 META-INF/MANIFEST.MF 的清单文件
-
jar 命令
- 创建 JAR 文件:jar cf test.jar -C dist/. 【cf :creat file】
- 创建 JAR 文件,并显式压缩过程 :jar cvf test.jar
- 不使用清单文件:jar cvfM test.jar
- 自定义清单文件内容:jar cvfm test.jar manifest.mf
- 查看 jar 包内容:jar tf test.jar
- 查看 jar 包详细内容:jar tvf test.jar
- 解压缩:jar xf test.jar
- 带提示信息解压缩:jar xvf test.jar