Java
- 1.Java重要特点
- 2.什么是JDK、JRE
- 3.注释
- 4.数据类型
- 5.编码
- 6.基本数据类型转换
- 7.逻辑运算符
- 8.原码反码补码
- 9.二维数组
- 10.对象内存布局
- 11.递归执行机制
- 12.方法重载
- 13.可变参数
- 14.作用域
- 15.构造器
- 16.对象创建的流程分析
- 17.this关键字
- 18.IDEA快捷键
- 19.包
- 20.访问修饰符
- 21.面向对象编程三大特征之一:封装
- 22.面向对象编程三大特征之一:继承
- 23.super关键字
- 24.方法重写(override)
- 25.面向对象编程三大特征之一:多态
- 26.Object类详解
- 27.断点调试
- 28.零钱通项目
- 29.类变量和类方法
- 30.代码块
- 31.设计模式:单例模式
- 32.final关键字
- 33.抽象类
- 34.设计模式:抽象模板模式
- 35.接口
- 36.内部类
- 37.枚举
- 38注解
- 39.异常
- 40.常用类
1.Java重要特点
- Java语言是面向对象的(oop)
- Java语言是健壮的。Java的强类型机制、异常处理、垃圾自动收集等是Java程序健壮性的重要保证
- Java语言是跨平台性的。(即:一个编译好的.class文件可以在多个系统下运行,这种特性称为跨平台)
- Java语言是解释型的。
解释型语言:javascript,PHP,java,不能直接被机器执行,需要解释器来执行,编译型语言,编译后的代码,可以直接被机器执行,C/C++。
2.什么是JDK、JRE
- JDK基本介绍
-
JDK的全称(Java Development Kit,Java开发工具包)
JDK=JRE + java的开发工具[java,javac,javadoc,javap等]
-
JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。
- JRE基本介绍
-
JRE(Java Runtime Environment,Java运行环境)
JRE = JVM + Java的核心类库[类]
-
包含Java虚拟机(JVM JAVA Virtual Machine)和Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
3.注释
- 单行注释: //
- 多行注释:/**/
4.数据类型
String是引用类型,对String进行操作,是创建新的对象(String的操作都是改变赋值地址而不是改变值操作)。对StringBuffer进行操作,这是在原来的对象之上进行改变(StringBuffer的操作都是改变值得操作)
5.编码
ASCII码:使用一个字节对英语字符与二进制位之间得关系做了统一规定,一共规定了128个字符的编码,只占用了一个字节的后面7位,最前面的1位统一规定为0。缺点是但是不能表示所有字符。
Unicode:一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,使用Unicode没有乱码的问题。缺点是一个英文字母和一个汉字都占用2个字节,这对于存储空间来说是浪费。2的16次方是65536,所以最多编码是65536个字符。编码0-127的字符是与ASCII的编码一样,因此Unicode码兼容ASCII码。
UTF-8:
- UTF-8是在互联网上使用最广的一种Unicode的实现方式(改进)
- UTF-8是一种变长的编码方式。它可以使用1-6个字节表示一个符号,根据不同的符号而变化字节长度
- 使用大小可变的编码 字母占1个字节,汉字占3个字节
6.基本数据类型转换
精度小的类型自动转换为精度大的数据类型,这个就是自动转换。(byte,short)和char之间不会相互自动转换,他们三者可以计算,在计算时首先转换为int类型。
- 基本类型转String
语法:基本类型的值+""即可
int n1 = 100;
float n2 = 1.1f;
String str1 = n1 + "";
String str2 = n2 + "";
System.out.println(str1 + " " + str2)
- String类型转基本数据类型
语法:通过基本类型的包装类调用parseXX方法即可
Integer.parseInt("123");
Double.parseDouble("123.1");
Float.parseFloat("123.45");
Short.parseShort("12");
Long.parseLong("12345");
Boolean.parseBoolean("true");
Byte.parseByte("12");
7.逻辑运算符
&&具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式
8.原码反码补码
9.二维数组
栈里的引用指向堆里的两个引用。
10.对象内存布局
首先把类信息加载到方法区,主要是属性和行为,然后分配空间,根据属性不一样分配不同的空间,如果是字符串,分配一个地址再指向常量池,如果是基本数据类型直接放在堆,最后把地址返回给对象。
从main栈开始,调用一个方法就开辟一个新的栈,用完就销毁掉。
11.递归执行机制
每次调用递归方法开辟一个新的栈。
- 例子一(老鼠出迷宫)
public class MiGong {
//编写一个main方法
public static void main(String[] args){
//思路
//1.先创建迷宫,用二维数组表示 int[][] map = new int[8][7];
//2.先规定map数组的元素值:0表示可以走,1表示障碍物
int[][] map = new int[8][7];
//3.将最上面的一行和最下面的一行,全部设置为1
for(int i = 0; i < 7; i++){
map[0][i] = 1;
map[7][i] = 1;
}
//4.将最左边的一列和最右边的一列,全部设置为1
for(int i = 0; i < 7; i++){
map[i][0] = 1;
map[i][6] = 1;
}
//剩余两个补齐
map[3][1] = 1;
map[3][2] = 1;
//输出当前地图
System.out.println("=============当前地图情况=============");
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
System.out.print(map[i][j] + " ");
}
System.out.println();
}
T2 t2 = new T2();
t2.find(map, 1, 1);
//输出当前地图
System.out.println("=============走迷宫后的地图情况=============");
for(int i = 0; i < map.length; i++){
for(int j = 0; j < map[i].length; j++){
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
}
class T2 {
//使用递归回溯的思想来解决老鼠出迷宫
//1.findWay方法就是专门来找出迷宫的路径
//2.如果找到,就返回ture,否则返回false
//3.map表示迷宫,i,j表示老鼠位置,初始化为(1,1)
//4.数字表示,0表示网格可走,1表示网格不可走,2表示网格走过,3表示走过不可走。
//5.当map[6][5] == 2说明走通,结束
public boolean find(int[][] map, int i, int j) {
if (map[6][5] == 2){
return true;
} else {
if (map[i][j] == 0) {
map[i][j] = 2;
if (find(map,i+1,j)) {
return true;
}else if (find(map,i,j+1)){
return true;
}else if(find(map,i-1,j)){
return true;
}else if (find(map,i,j-1)){
return true;
}else {
map[i][j] = 3;
return false;
}
}else {
return false;
}
}
}
}
- 例子二(汉诺塔)
public class HanoiTower {
//编写一个main方法
public static void main(String[] args) {
T3 t3 = new T3();
t3.move(3,'a','b','c');
}
}
class T3 {
//方法
//num表示要移动的个数,a,b,c,分别表示A塔,B塔,C塔
public void move(int num, char a,char b, char c) {
//如果只有一个盘
if (num == 1) {
System.out.println(a + "->" + c);
} else {
//如果有多个盘。可以看成两个,最下面的和上面你的所有盘
//1.先移动上面所有的盘到b,借助c
move(num - 1, a, c, b);
//2.把最下面的盘,移动到c
System.out.println(a + "->" + c);
//3.再把b上的所有盘移动到c,借助a
move(num - 1, b, a, c);
}
}
}
递归调用流程图如下:
12.方法重载
定义:java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致!重载可以减轻起名和记名的麻烦。
public class Overload {
public static void main(String[] args) {
T4 t4 = new T4();
t4.m(1, 2, 3);
}
}
class T4 {
public void m (int n1, int n2) {
System.out.println(n1+n2);
}
public void m (int n1, double n2) {
System.out.println(n1+n2);
}
public void m (int n1, int n2, double n3) {
System.out.println(n1 + n2 + n3);
}
}
13.可变参数
定义:java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
- 注意事项和使用细节
- 可变参数的试产可以为0个或任意多个
- 可变参数的实参可以为数组
- 可变参数的本质是数组
- 可变参数可以和普通类型的参数一起放在形参列表,但保证可变参数在最后
- 一个形参列表中只能出现一个可变形参
public class Overload {
public static void main(String[] args) {
// T4 t4 = new T4();
// t4.m(1, 2, 3);
HspMethod n = new HspMethod();
System.out.println(n.sum());
}
}
class HspMethod {
//计算多个数的和
//可以使用方法重载
public int sum(int n1, int n2) {
//2个数的和
return n1 + n2;
}
public int sum(int n1, int n2, int n3) {
//3个数的和
return n1 + n2 +n3;
}
public int sun(int n1, int n2, int n3, int n4) {
//4个数的和
return n1 + n2 +n3 +n4;
}
//上面三个方法名称相同,功能相同,参数个数不同->使用可变参数优化
//1.int...表示接收的是可变参数,类型是int,即可以接收多个int(0-多)
//2.使用可变参数时,可以当作数组来使用,即nums可以当作数组
public int sum(int... nums) {
int sum = 0;
System.out.println("接收的参数个数=" + nums.length);
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
}
14.作用域
- java编程中,主要的变量就是属性(成员变量)和局部变量。
- 局部变量一般是指在成员方法中定义的变量。
- java中作用域的分类
全局变量:也就是属性,作用域为整个类体
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中! - 全局变量(属性)可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能用,因为没有默认值。
注意事项和细节使用:
1.属性和局部变量可以重名,访问时遵循就近原则。
2.在同一个作用域中,比如在同一个成员方法中,两个局部变量不能重名。
3.属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。即在一次方法调用过程中。
4.作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过对象调用)
局部变量:只能在本类中对应的方法中使用
5.修饰符不同
全局变量/属性可以加修饰符
局部变量不可以加修饰符
15.构造器
定义:用类的一种特殊的方法,它的主要作用是完成对对象的初始化。
规定:
- 构造器的修饰符可以默认
- 构造器没有返回值
- 方法名和类名必须一样
- 参数列表和成员方法一样的规则
- 构造器的调用,由系统完成
注意事项和使用细节:
- 构造器是完成对象的初始化,并不是创建对象
- 程序没有定义构造方法,系统会自动给类生成一个默认无参构造方法。
- 一旦定义了自己的构造器,默认的无参构造器就被覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下。
public class Constructor {
//编写一个main方法
public static void main(String[] args) {
Person1 p = new Person1("Ryan_小王", 23);
System.out.println("姓名:" + p.name +"\n年龄:" + p.age);
Person1 p2 = new Person1("小王");
System.out.println("第二个构造器:" + "姓名:" + p2.name);
}
}
class Person1 {
String name;
int age;
//第一个构造器
public Person1(String pName, int pAge) {
name = pName;
age = pAge;
}
//第二个构造器
public Person1(String pName) {
name = pName;
}
}
16.对象创建的流程分析
17.this关键字
定义:java虚拟机会给每个对象分配this,代表当前对象。哪个对象调用,this就代表哪个对象。
public class Constructor {
//编写一个main方法
public static void main(String[] args) {
Dog p = new Dog("小黄", 3);
System.out.println("姓名:" + p.name +"\n年龄:" + p.age);
System.out.println("对象的hashcode:" + p.hashCode());
p.speak();
System.out.println("===========================================");
Dog p2 = new Dog("大黄", 4);
System.out.println("姓名:" + p2.name +"\n年龄:" + p2.age);
System.out.println("对象的hashcode:" + p2.hashCode());
p2.speak();
}
}
class Dog {
String name;
int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
System.out.println("this的hashcode:" + this.hashCode());
}
public void speak() {
System.out.println("I can speak!");
System.out.println("this的hashcode:" + this.hashCode());
//se
}
}
结果表明:哪个对象调用,this就代表哪个对象。
this的hashcode:356573597
姓名:小黄
年龄:3
对象的hashcode:356573597
I can speak!
this的hashcode:356573597
===========================================
this的hashcode:1735600054
姓名:大黄
年龄:4
对象的hashcode:1735600054
I can speak!
this的hashcode:1735600054
this的注意事项和使用细节:
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表)
- 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器,必须放在第一条语句)
- this不能在类定义的外部使用,只能在类定义的方法中使用
//使用细节3代码研是
public class Constructor {
//编写一个main方法
public static void main(String[] args) {
T1 t = new T1();
t.eat();
}
}
class T1 {
public void speak() {
System.out.println("hello");
}
public void eat() {
System.out.println("eat");
//第一种方式
speak();
//第二种方式
this.speak();
}
}
//使用细节4代码演示
public class Constructor {
//编写一个main方法
public static void main(String[] args) {
T1 t = new T1();
}
}
class T1 {
public T1() {
//这里去访问T(String name, int age)构造器
this("jack", 200);
System.out.println("T()构造器");
}
public T1(String name, int age) {
System.out.println("T(String name, int age)构造器");
}
}
18.IDEA快捷键
19.包
20.访问修饰符
21.面向对象编程三大特征之一:封装
定义:封装就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
代码示例如下:
package com.xiaoming;
public class Person {
public static void main(String[] args) {
T t = new T();
t.setName("小王");
t.setAge(300);
t.setSalary(30000);
System.out.println(t.info());
T t1 = new T("小彭sadasd",27,100);
System.out.println(t1.info());
}
}
class T {
public String name;
private int age;
private int salary;
public T() {
}
public T(String name, int age, int salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
setName(name);
setAge(age);
setSalary(salary);
}
public String getName() {
return name;
}
public void setName(String name) {
//加入业务逻辑
if (name.length() >= 2 && name.length() <= 6) {
this.name = name;
} else {
System.out.println("名字的长度必须在2-6之间");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0 && age <= 150) {
this.age = age;
} else {
System.out.println("年龄必须在0-150之间");
}
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public String info() {
return "姓名:" + name + "\t" + "年龄:" + age + "\t"+ "薪水" + salary;
}
}
封装与构造器:为了不让防护机制失效,可以将set方法写入构造器。
public T(String name, int age, int salary) {
// this.name = name;
// this.age = age;
// this.salary = salary;
setName(name);
setAge(age);
setSalary(salary);
}
22.面向对象编程三大特征之一:继承
继承本质详解:
代码如下所示:
package com.xiaoming;
public class ExtendTheory {
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.name);
}
}
class GrandPa {
String name = "爷爷";
String hobby = "旅游";
}
class Father extends GrandPa {
String name = "大头爸爸";
int age = 39;
}
class Son extends Father {
String name = "大头儿子";
}
//结果
大头儿子
访问规则:
注意:若属性访问不到,可以通过父类提供的公共方法去访问。
内存加载机制:
先在方法去加载类信息,从顶级父类开始加载,再在堆内分配内存,基本数据类型直接存,字符串常量放在常量池并返回地址,最终将对象地址返回到栈中 。
23.super关键字
24.方法重写(override)
重载和重写的比较:
25.面向对象编程三大特征之一:多态
-
多态的基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态实建立在封装和继承基础之上的。 -
多态的具体体现
1.重写和重载就体现多态
2. 对象的多态(核心,困难,重点)
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的
(4)编译类型看定义时 =号的左边,运行类型看=号的右边
编译类型一直是Animal,运行类型从Dog变成了Cat。
Animal.java
package com.poly;
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Dog.java
package com.poly;
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
Cat.java
package com.poly;
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
Food.java
package com.poly;
public class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Bone.java
package com.poly;
public class Bone extends Food {
public Bone(String name) {
super(name);
}
}
Fish.java
package com.poly;
public class Fish extends Food{
public Fish(String name) {
super(name);
}
}
Master.java
package com.poly;
public class Master {
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//主人给小狗喂骨头
public void feed(Dog dog, Bone bone) {
System.out.println("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
}
//主人给小猫喂小黄鱼
public void feed(Cat cat, Fish fish) {
System.out.println("主人" + "给" + cat.getName() + "吃" + fish.getName());
}
}
Poly.java
package com.poly;
public class Poly {
public static void main(String[] args) {
Master tom = new Master("汤姆");
Dog dog = new Dog("大黄");
Bone bone = new Bone("骨头");
tom.feed(dog, bone);
System.out.println("===================================");
Cat cat = new Cat("小花猫");
Fish fish = new Fish("小黄鱼");
tom.feed(cat, fish);
}
}
结果:
主人汤姆给大黄吃骨头
===================================
主人给小花猫吃小黄鱼
问题:如果有很多动物,很多种食物,就要在Master中重载很多feed()方法,多态的一种体现在对象的多态,可以对feed()进行如下操作:
// //主人给小狗喂骨头
// public void feed(Dog dog, Bone bone) {
// System.out.println("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
// }
//
// //主人给小猫喂小黄鱼
// public void feed(Cat cat, Food fish) {
// System.out.println("主人" + "给" + cat.getName() + "吃" + fish.getName());
// }
//使用多态机制,可以统一管理主人喂食的问题
//animal编译类型实Animal,可以指向(接收)Animal子类的对象
//food编译类型是Food,可以指向(接收)Food子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
}
多态的向上转型
多态的向下转型
多态属性
属性:
package com.poly;
public class Test{
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub(); //向上转型
System.out.println(base.count); //编译类型是Base,所以结果为10
}
}
class Base { //父类
int count = 10; //属性
}
class Sub extends Base { //子类
int count = 20; //属性
//结果
//10
instanceOf比较操作符:
package com.poly;
public class Test2{
public static void main(String[] args) {
BB bb = new BB();
//aa编译类型AA,运行类型是BB
AA aa = new BB();
System.out.println(bb instanceof BB); //true
System.out.println(aa instanceof BB); //true,因为判断的是运行类型
}
}
class Base {} //父类
class Sub extends Base {} //子类
练习题:
动态绑定机制
package com.poly;
public class Test {
public static void main(String[] args) {
//运行类型是B
A a = new B();
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
// public int sum() {
// return i + 20;
//}
//public int sum1() {
// return i + 10;
//}
public int getI() {
return i;
}
}
//B的sum和sum1两个方法未注释掉时候,输出结果为40,30
//注释掉B中的sum和sum1两个方法,输出结果为30,20
//原因:对象的方法会有动态绑定机制,而属性没有动态绑定机制,哪里声明,哪里使用,在子类B中未找到sum(),到A中找到了sum(),再使用B的getI() 得到i=20,返回20+10=30。
多态数组
Person[] person = new Person[5];
person[0] = new Person("Jack", 20);
person[1] = new Student("mary", 18, 100);
person[2] = new Student("smith", 19, 66);
person[3] = new Teacher("scott", 30, 20000);
person[4] = new Teacher("ryan", 23, 30000);
多态参数
Employee.java
package com.poly;
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
//得到年工资的方法
public double getAnnual() {
return 12 * salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
Worker.java
package com.poly;
public class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工:" + getName() + "正在工作");
}
@Override
public double getAnnual() { //因为普通员工没有其他收入,则直接调用父类方法
return super.getAnnual();
}
}
Manager.java
package com.poly;
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void manager() {
System.out.println("经理" + getName() + "正在管理");
}
// 重写年薪方法
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
PloyParameter.java
package com.poly;
public class PloyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
PloyParameter e = new PloyParameter();
e.showEmpAnnual(tom);
e.showEmpAnnual(milan);
e.testWork(tom);
e.testWork(milan);
}
//实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
//添加一个方法。testWork,若是普通员工,调用work(),若是经理,调用manage()
public void testWork(Employee e) {
if (e instanceof Worker) {
((Worker) e).work();
} else if (e instanceof Manager) {
((Manager) e).manager();
} else {
System.out.println("不做处理!");
}
}
}
结果如下:
30000.0
260000.0
普通员工:tom正在工作
经理milan正在管理
26.Object类详解
1.==和equals的对比
- ==:既可以判断基本类型,又可以判断引用类型
- ==:如果判断基本类型,判断的是值是否相等。示例:int i = 10’ dpuble d =10.0;
- ==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
- equals:是Object类中的方法,只能判断引用类型
- equals默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。【看看String和Integer的equals源代码】
练习1:重写equals方法,判断两个对象内容是否相等,如果两个对象各个属性值都一样则返回true,反之false。
package com.poly;
public class Test2 {
public static void main(String[] args) {
Person tom = new Person("tom", 30);
Person mike = new Person("tom", 30);
System.out.println(tom.equals(mike));
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public boolean equals(Object p) {
//如果两个是同一个对象则直接返回true
if (this == p) {
return true;
} else if (p instanceof Person) { //类型判断,是Person才比较
//向下转型, 因为我们需要p的各个属性
Person p1 = (Person) p;
return this.name.equals(p1.name) && this.age == p1.age;
} else {
return false;
}
}
}
练习2:==和equals判断
Person p1 = new Person();
p1.name = "xiaowang";
Person p2 = new Person();
p2.name = "xiaowang";
String s1 = new String("xiaowang");
String s2 = new String("xiaowang");
System.out.println(p1.equals(p2)); //false,因为Person没有重写equals,默认为Object的equals,直接比较两个对象是否相同
System.out.println(s1.equals(s2)); //true,String重写了equals,直接比较字符串的内容
System.out.println(p1.name.equals(p2.name)); //true,String重写了equals,直接比较字符串的内容
System.out.println(p1 == p2); // false,==的使用:基本类型判断值,引用类型判断地址即对象
System.out.println(s1 == s2); //false
练习3:
2.hashcode方法
3.toString方法
4.finalize方法
package com.poly;
public class Test_GC {
public static void main(String[] args) {
Car bmw = new Car("宝马");
bmw = null;
System.gc(); //主动调用垃圾回收器
System.out.println("程序退出!");
}
}
class Car {
private String name;
public Car(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁汽车" + name);
System.out.println("我们释放了某些资源。。。");
}
public void say() {}
}
结果:
程序退出!
我们销毁汽车宝马
我们释放了某些资源。。。
27.断点调试
28.零钱通项目
1.面向过程版本
SmallChangeSys .java
package com.smallchangesys;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class SmallChangeSys {
public static void main(String[] args) {
//定义相关变量
boolean loop = true;
Scanner scanner = new Scanner(System.in);
String key = "";
//2.零钱通明细
String details = "----------------零钱通明细--------------";
//3.完成收益入账
double money = 0;
double balance = 0;
Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");//可以用于日期格式化的
//4.消费
String note = "";
do {
System.out.println("\n===========零钱通菜单===========");
System.out.println("\t\t\t1 零钱通明细");
System.out.println("\t\t\t2 收益入账");
System.out.println("\t\t\t3 消费");
System.out.println("\t\t\t4 退 出");
System.out.println("请选择(1-4):");
key = scanner.next();
//使用switch分支控制
switch (key) {
case "1":
System.out.println(details);
break;
case "2":
System.out.println("收益入账金额:");
money = scanner.nextDouble();
//money的值范围应该校验-》一会完善
balance += money;
//拼接收益入账信息到details
date = new Date(); //获取当前日期
details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance ;
break;
case "3":
System.out.println("消费金额:");
money = scanner.nextDouble();
System.out.println("消费说明:");
note = scanner.next();
balance -= money;
//拼接消费信息到details
date = new Date();
details += "\n" + note +"\t-" + money + "\t" + sdf.format(date) + "\t" + balance;
break;
case "4":
String choice = "";
while (true) {
System.out.println("你确定要退出吗? y/n");
choice = scanner.next();
if ("y".equals(choice) || "n".equals(choice)) {
break;
}
}
//当前用户退出while,进行判断
if (choice.equals("y")) {
loop = false;
}
break;
default:
System.out.println("选择有误,请重新选择");
}
}while (loop);
}
}
2.OOP版本
SmallChangeSysOOP .java
package com.smallchangesys;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class SmallChangeSysOOP {
//定义相关变量
boolean loop = true;
Scanner scanner = new Scanner(System.in);
String key = "";
//2.零钱通明细
String details = "----------------零钱通明细--------------";
//3.完成收益入账
double money = 0;
double balance = 0;
Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");//可以用于日期格式化的
//4.消费
String note = "";
//先完成显示菜单,并可以选择
public void mainMenu() {
do {
System.out.println("\n===========零钱通菜单(OOP)===========");
System.out.println("\t\t\t1 零钱通明细");
System.out.println("\t\t\t2 收益入账");
System.out.println("\t\t\t3 消费");
System.out.println("\t\t\t4 退 出");
System.out.println("请选择(1-4):");
key = scanner.next();
//使用switch分支控制
switch (key) {
case "1":
this.detail();
break;
case "2":
this.income();
break;
case "3":
this.pay();
break;
case "4":
this.exit();
break;
default:
System.out.println("选择有误,请重新选择");
}
}while (loop);
}
//完成零钱通明细
public void detail() {
System.out.println(details);
}
//完成收益入账
public void income() {
System.out.println("收益入账金额:");
money = scanner.nextDouble();
//money的值范围应该校验-》一会完善
if (money <= 0) {
System.out.println("收益入账金额应该大于0");
return; //退出方法,不在执行后面的代码
}
balance += money;
//拼接收益入账信息到details
date = new Date(); //获取当前日期
details += "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance ;
}
//完成消费
public void pay() {
System.out.println("消费金额:");
money = scanner.nextDouble();
System.out.println("消费说明:");
note = scanner.next();
balance -= money;
//拼接消费信息到details
date = new Date();
details += "\n" + note +"\t-" + money + "\t" + sdf.format(date) + "\t" + balance;
}
//退出
public void exit() {
String choice = "";
while (true) {
System.out.println("你确定要退出吗? y/n");
choice = scanner.next();
if ("y".equals(choice) || "n".equals(choice)) {
break;
}
}
//当前用户退出while,进行判断
if (choice.equals("y")) {
loop = false;
}
}
}
测试类SmallChangeApp .java
package com.smallchangesys;
/**
* 这里我们直接调用SmallChangeSysOOP对象,显示主菜单即可
*/
public class SmallChangeApp {
public static void main(String[] args) {
new SmallChangeSysOOP().mainMenu();
}
}
29.类变量和类方法
1.类变量
定义:类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它的时,修改的也是同一个变量。
静态变量存储位置:JDK7以下版本静态域在方法区,JDK7以上版本,静态域存储于定义类型的Class对象中,Class对象如同堆中其他对象一样,存在于GC堆中。
共识:
(1)static变量是同一个类所有对象共享
(2)static类变量,在类加载的时候就生成了
访问类变量:
注:类变量是随着类的加载而创建的,所以即使没有创建对象实例也可以访问。
类变量使用注意细节:
2.类方法
使用场景:
注意事项:
3.理解main方法
cmd中给main方法传参数:
IDEA中给main方法传参:
30.代码块
1.代码块介绍
代码块相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化操作,因为代码块的调用顺序优于构造器,会在构造器之前输出代码块内容,普通代码块创建对象时调用,创建一次,调用一次,静态代码块是类加载时执行,且只会执行一次,类加载有三种情况,第一种是对象实例时,第二种是创建子类对象实例时,父类也会被加载,第三种是使用类的静态成员时。
2.代码块使用细节
1.静态代码块和普通代码块的加载,以及类加载的三种情况
2.创建一个对象,在一个类中的调用顺序,(静态代码块和静态属性初始化)->(普通代码块和普通属性的初始化)->(构造方法)
3.构造器的前面隐含了super()和调用普通代码块
4.创建子类对象(继承关系),静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序
创建对象时候首先是类加载,会调用静态代码块和静态属性初始化,然后才是创建对象实例,这时候会调用构造方法,构造方法隐含了super()和调用普通代码块、普通属性初始化。
31.设计模式:单例模式
1.饿汉式
就是可能还没有用到对象的时候,随着类的加载,比如直接用类访问静态属性导致类的加载,这样就会致使对象创建好了,所以叫饿汉式。
package com.single_;
public class SingleTon01 {
public static void main(String[] args) {
GirlFriend gc = GirlFriend.getInstance();
System.out.println(gc);
}
}
//1.将构造器私有化,这样在别的类中就不能直接new一个对象
//2.在类的内部直接创建一个对象
//3.提供一个公共的static方法,返回gf对象,定义为static类型的原因是:若不是static就要new一个对象才能调用该方法
class GirlFriend {
private String name;
public static int i = 1;
//1.将构造器私有化,这样在别的类中就不能直接new一个对象
private GirlFriend(String name) {
// System.out.println("调用了");
this.name = name;
}
//2.在类的内部直接创建一个对象
private static GirlFriend gf= new GirlFriend("小红");
//3.提供一个公共的static方法,返回gf对象
public static GirlFriend getInstance() {
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
2.懒汉式
懒汉式是在使用对象时才创建对象。
package com.single_;
public class SingleTon02 {
public static void main(String[] args) {
System.out.println(Cat.getInstance());
// System.out.println(Cat.i);
}
}
class Cat {
private String name;
public static int i = 1;
private static Cat cat;
//步骤
//1.仍然构造器私有化
//2.定义一个static静态属性对象
//3.提供一个public的static方法,可以返回一个Cat对象
private Cat(String name) {
// System.out.println("调用了");
this.name = name;
}
public static Cat getInstance() {
if (cat == null) {
cat = new Cat("小白");
}
return cat;
}
public String toString() {
return "Cat{name='" + name + "'}";
}
}
3.饿汉式和懒汉式区别
32.final关键字
1.基本介绍
2.注意事项
注:第七点中,若不用final修饰,调用Demo.i的时候会导致类的加载进而先执行静态代码块的内容,而在static前面加上final修饰的时候,再输出Demo.i的时候,就会直接输出结果,而不导致类加载。
33.抽象类
1.抽象类介绍
2.抽象类使用注意事项和细节讨论
package com.abstract_;
public class AbstarctDetail01 {
//1.抽象类不能被实例化,此语句报错
new A();
}
//2.抽象类不一定要有abstract方法,还可以有实现的方法
abstract class A {
public void hi() {
System.out.println("hi");
}
}
//3.一旦包含了abstract方法,则这个类必须声明为abstract
abstract class B {
public abstract void hi();
}
//4.abstract只能修饰类和方法,不能修饰属性和其它的
class C {
// public abstract int n1 = 1; //这样是错误的
}
//7.如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,
//除非它自己也声明为abstract类
abstract class A {
public abstract void hi();
}
//实现A的所有抽象方法,所谓实现方法,就是有方法体
class B extends A {
@Override
public void hi() {
}
}
//自己也声明为抽象类
abstract class C extends A { }
34.设计模式:抽象模板模式
计算不同job完成的时间。
1.Template.java
package com.abstract_;
abstract public class Template {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
public abstract void job();
public void calculateTime() {
//得到开始时间
long start = System.currentTimeMillis();
job();
//得到结束时间
long end = System.currentTimeMillis();
System.out.println("执行时间:"+ (end - start));
}
}
2.AA.java
package com.abstract_;
public class AA extends Template {
@Override
public void job() {
int num = 0;
for (int i = 0; i <= 10000; i++) {
num += i;
}
}
}
3.BB.java
package com.abstract_;
public class BB extends Template {
@Override
public void job() {
int num = 0;
for (int i = 0; i <= 800000; i++) {
num *= i;
}
}
}
35.接口
1.定义:接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。
类使用接口的时候必须实现接口的抽象方法,接口的方法默认为abstract修饰,在接口中abstract可写可不写。
小结:
1.在jdk7.0前,接口里的所有方法都没有方法体。即都是抽象方法。
2.Jdk8.0后接口可以有静态方法,默认方法(default修饰方法),也就是说接口中可以有方法的具体实现。
2.使用细节
课堂练习:
3.接口和继承
package com.interface_;
public class Monkey {
public static void main(String[] args) {
LittleMonkey littleMonkey = new LittleMonkey("悟空");
littleMonkey.m();
littleMonkey.swimming();
littleMonkey.fly();
}
public String name;
public Monkey(String name) {
this.name = name;
}
public void m() {
System.out.println(name + "会爬树");
}
public String getName() {
return name;
}
}
//继承
class LittleMonkey extends Monkey implements A,B {
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + "可以像🐟一样游泳。。。。");
}
@Override
public void fly() {
System.out.println(getName() + "可以像鸟一样飞翔。。。");
}
}
//接口
interface A {
void swimming();
}
interface B {
void fly();
}
4.接口和抽象类
//定义接口,含有钻火圈方法
public interface DrillFireCircle() {
public abstract void drillFireCircle();
}
//定义抽象类狗类
public abstract class Dog {
public abstract void eat();
public abstract void sleep();
}
//继承抽象类且实现接口
class SpecialDog extends Dog implements drillFireCircle {
public void eat() {
//....
}
public void sleep() {
//....
}
public void drillFireCircle() () {
//....
}
}
继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如狗是否能钻火圈,能则可以实现这个接口,不能就不实现这个接口。
抽象类可以有不是抽象方法的方法,这样的类就不能算作纯粹的接口,只能说抽象类是普通类与接口之间的一种中庸之道。
接口与抽象类的不同之处在于:
1、抽象类可以有方法体的方法,但接口没有。
2、接口中的成员变量隐式为 public static final,但抽象类不是的。
3、一个类可以实现多个接口,但只能继承一个抽象类。
4.接口多态传递
1.电脑Usb接口的案例体现多态
//1.interface_
package com.interface_;
public interface Interface_c {
void start();
void stop();
}
//2.Camera.java
package com.interface_;
public class Camera implements Interface_c {
@Override
public void start() {
System.out.println("照相机开始拍照");
}
@Override
public void stop() {
System.out.println("照相机关闭拍照");
}
}
//3.Phone.java
package com.interface_;
public class Phone implements Interface_c{
@Override
public void start() {
System.out.println("手机开始拍照");
}
@Override
public void stop() {
System.out.println("手机关闭拍照");
}
}
//4.Computer
package com.interface_;
public class Computer {
public static void main(String[] args) {
Computer computer = new Computer();
computer.use_c(new Camera());
System.out.println("=========================" );
computer.use_c(new Phone());
}
public void use_c(Interface_c interface_c) {
interface_c.start();
interface_c.stop();
}
}
2.多态传递现象
package com.interface_;
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的对象实例
IG ig = new Teacher();
//如果IG继承了IH接口,而Teacher类实现了IG接口
//那么,实际上就相当于Teacher类也实现了IH接口
//这就是所谓的多态传递现象
IH ih = new Teacher();
}
}
interface IH {}
interface IG extends IH{}
class Teacher implements IG {
}
36.内部类
1.局部内部类的使用
package com.innerclass;
public class innerclass01 {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
}
}
class Outer02 { //外部类
private int n1 = 100;
private void m2() {
System.out.println("Outer02");
} //私有方法
public void m1() { //方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用final修饰
//4.作用域:仅仅在定义它的方法或代码块中
final class Inner02 { //局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
public void f1() {
//5.局部内部类可以直接访问外部类的成员
System.out.println("n1=" + n1);
m2();
}
}
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
2.匿名内部类
1.//基于接口的匿名内部类//演示基于类的匿名内部类
package com.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 { //外部类
private int i =10;
public void method() { //方法
//基于接口的匿名内部类
//1.需求:想要使用IA接口,并创建对象
//2.传统方法,是写一个类,实现接口,并创建对象
//3.需要Tiger/Dog类只使用一次,后面再不使用
//4.可以使用匿名内部类来简化开发
//5.tiger的编译类型 ? IA
//6.tiger的运行类型 ? 就是匿名内部类 Outer04$1
//7.底层会分配Outer04$1对象
/*
class Outer04$1 implement IA {
@Override
public void cry() {
System.out.println("老虎叫唤。。。");
}
}
*/
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤。。。");
}
};
//7.jdk底层在创建匿名内部类Outer04$1,立即马上创建了Outer04$1实例,
// 并把地址返回给了tiger
//8.匿名内部类使用一次,就不能再使用,但对象还是可以一直调用的
tiger.cry();
tiger.cry();
//演示基于类的匿名内部类
//1.father编译类型Father
//2.father运行类型Outer04$2
//3.底层会创建匿名内部类
/*
class Outero4$2 extends Father{
@Override
public void test() {
System.out.println("重写了父类的test()");
}
}
*/
//4.同时也直接返回了匿名内部类Outer04$2的对象
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("重写了父类的test()");
}
};
System.out.println(father.getClass()); //Outer04$2
father.test();
}
}
interface IA { //接口
public void cry();
}
class Father {
private String name;
public Father(String name) {
this.name = name;
}
public void test() { //方法
}
}
2.使用注意细节
3.匿名内部类实践
package com.innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,简洁高效
method(new A() {
@Override
public void say() {
System.out.println("画画的baby");
}
});
}
//静态方法
public static void method(A a) {
a.say();
}
}
//接口
interface A {
void say();
}
package com.innerclass;
public class InnerClassExercise02 {
}
//接口
interface Bell {
void ring();
}
class CellPhone {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
//1.
cellPhone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了");
}
});
//2.
cellPhone.alarmclock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了。。");
}
});
}
public void alarmclock(Bell bell) {
bell.ring();
}
}
3.成员内部类
package com.innerclass;
public class MemberClass {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.test();
}
}
class Outer05 {
public int i = 10;
private int age = 30;
class Inner {
public void say() {
System.out.println("i = " + i + "\tage = " + age);
}
}
public void test() {
Inner inner = new Inner();
inner.say();
}
}
第6点举例说明如下:
4.静态内部类
37.枚举
1.自定义枚举
package com.enum_;
import java.util.Scanner;
/**
* Created on 2021/6/18.
*
* @author Ryan_小王
*/
public class Enumeration01 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
System.out.println(Season.AUTUNM);
System.out.println(Season.WINTER);
}
}
class Season {
private String name;
private String desc;
private Season(String name, String desc) {
this.name =name;
this.desc =desc;
}
public final static Season SPRING = new Season("春天","温暖");
public final static Season SUMMER = new Season("夏天","炎热");
public final static Season AUTUNM = new Season("秋天","凉爽");
public final static Season WINTER = new Season("冬天","寒冷");
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
2.enum枚举类
1.使用enum实现枚举类
2.public final static Season SPRING = new Season(“春天”,“温暖”);直接使用SPRING(“春天”,“温暖”)替代
3.如果有多个常量(对象),使用,号间隔即可
4.如果使用enum来实现枚举,要求将定义常量对象,写在前面
package com.enum_;
import java.util.Scanner;
/**
* Created on 2021/6/18.
*
* @author Ryan_小王
*/
public class Enumeration01 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
}
}
enum Season {
//1.使用enum实现枚举类
//2.public final static Season SPRING = new Season("春天","温暖");
//直接使用SPRING("春天","温暖")替代
//3.如果有多个常量(对象),使用,号间隔即可
//4.如果使用enum来实现枚举,要求将定义常量对象,写在前面
SPRING("春天","温暖"),SUMMER("夏天","炎热");
private String name;
private String desc;
private Season(String name, String desc) {
this.name =name;
this.desc =desc;
}
// public final static Season SPRING = new Season("春天","温暖");
// public final static Season SUMMER = new Season("夏天","炎热");
// public final static Season AUTUNM = new Season("秋天","凉爽");
// public final static Season WINTER = new Season("冬天","寒冷");
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
3.enum关键字实现枚举类注意事项
4.enum常用方法
package com.enum_;
/**
* Created on 2021/6/18.
*
* @author Ryan_小王
*/
public class EnumMethod {
public static void main(String[] args) {
//使用Season枚举类,来演示各种方法
Season summer = Season.SUMMER;
//返回对象名
System.out.println(summer.name());
//输出该枚举对象的次序/编号,从0开始编号
System.out.println(summer.ordinal());
//含有定义的所有枚举对象
Season[] values = Season.values();
for (Season season: values) {
System.out.println(season);
}
//valueof:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则异常
Season summer1 = Season.valueOf("SUMMER");
System.out.println("summer1=" + summer1);
//比较两个枚举常量的编号,此处为0-1=-1,所以结果为输出-1
System.out.println(Season.SPRING.compareTo(Season.SUMMER));
}
}