Java学习笔记——面向对象

三条主线:

1、Java类及类的成员:属性、方法、构造器;代码块、内部类

2、面向对象三大特征:封装、继承性、多态性、(抽象性)

3、其他关键字:this、super、static、final、abstract、interface、package、import等

面向对象设计的三大特征六大原则

面向对象三大特征:

封装、继承性、多态性

面向对象六大原则:

单一职责原则

单一职责原则的定义是:就一个类而言,应该只有一个引起它变化的原因。简单来说,一个类应该是一组相关性很高的函数和数据的封装。

开闭原则

开闭式原则的定义:软件中的对象应该对扩展是开放的,但是,对修改是关闭的。

里氏替换原则

里氏替换原则的定义:所有引用基类的地方都必须能够透明的使用其子类。

依赖倒置原则

依赖倒置原则指的是高层次模块不应该依赖于低层次模块的具体实现,两者都应该依赖其抽象

接口隔离原则

接口隔离原则的定义:类间的依赖关系应该建立在最小的接口之上迪米特法则

迪米特原则

一个对象应该对其他对象有最少的了解。一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的一概不关心。

属性与方法:

属性=成员变量=field=域、字段

方法=成员方法=函数=method

方法的使用中,可以调用当前类的属性和方法

方法中不能定义方法

属性

属性vs局部变量

1.相同点:

​ 1.1定义变量的格式:数据类型 变量名=变量值;

​ 1.2先声明后使用

​ 1.3变量都有其对应的作用域

2.不同点

​ 2.1在类中声明的位置的不同

​ 属性:直接定义在类的一对{}内

​ 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量

​ 2.2关于权限修饰符的不同

​ 属性:声明属性时,指明其权限,使用权限修饰符

​ 局部变量:不使用权限修饰符

​ 2.3默认初始值的情况

​ 属性:类的属性根据其类型都有默认初始值

​ 整型(byte、short、int、long:0)

​ 浮点型(float、double:0.0)

​ 字符型(char:0(或者’\u0000’))

​ 布尔型(boolean:false)

​ 引用数据类型(类、数组、接口:null)

​ 局部变量:没有默认初始值,所以要显式赋值

​ 2.4在内存中加载的位置

​ 属性:加载在堆空间中(非static)

​ 局部变量:加载在栈空间中

3.变量的分类

​ 3.1按照数据类型
Java学习笔记——面向对象_第1张图片

​ 3.2按照在类中声明的位置

Java学习笔记——面向对象_第2张图片

JVM内存结构与对象内存解析:

编译完源代码以后,生成一个或者多个字节码文件。

我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。

Java学习笔记——面向对象_第3张图片

虚拟机栈,即为平时提到的栈结构。我们将局部变量存储在栈结构中

堆:我们将new出来的结构(比如:数组、对象)加载在堆空间中。补充:对象的属性(非static)加载在堆空间中

方法区:类的加载信息、常量池、静态域

(引用类型的变量:存放两类值:null和地址(含变量的类型))

万事万物皆对象

1.在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化来调用具体的功能结构。

2.涉及到java语言与前端html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。

对象数组的内存解析

Java学习笔记——面向对象_第4张图片

匿名对象的使用

1.理解:我们创建的对象没有显式赋给一个变量名即为匿名对象

2.特征:匿名对象只能调用一次

3.使用

package a01;

public class Test3 {
	public static void main(String[] args) {
		new Phone().price = 1999;
		System.out.println(new Phone().price);//此处新创建了对象
		//匿名对象的使用
		PhoneMall mall = new PhoneMall();
		mall.use(new Phone());//虽然此处没有将其赋给变量名,但是传参过程中将其传给了形参phone
	}
}

class PhoneMall{
	public void use(Phone phone) {
		phone.sendEmail();
		phone.playGame();
	}
}

class Phone{
	
	public double price;
	
	public void playGame() {
		System.out.println("玩游戏");
	}
	
	public void sendEmail() {
		System.out.println("写邮件");
	}
}

方法的重载

两同一不同:同一个类、相同方法名

​ 参数列表不同

跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系。

可变个数形参的方法

1.可变个数形参的格式:数据类型 … 变量名

2.当调用可变个数形参的方法时,传入的参数可以是0个、1个、2个……

3.可变个数形参的方法与本类中方法名相同但形参不同的方法之间构成重载

4.可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。二者不能共存

5.可变个数形参在方法的形参列表中必须声明在末尾

6.可变个数形参在方法的形参列表中最多只能声明一个可变形参

package a01;

public class Test1 {
	public static void main(String[] args) {
		Test1 t = new Test1();
		t.show(1);
		t.show("hello");
		t.show("hello","world");//当方法为可变个数形参形式采取这种形式
		t.show(new String[] {"hello","world"});//当方法为数组形式调用时采取这种形式
	}
	
	public void show(int i) {
		System.out.println("int类型的show方法");
	}
	
	public void show(String s) {
		System.out.println("String类型的show方法");
	}
	
//	public void show(String[] s) {
//		System.out.println("String类型数组方法");
//	}
	
	public void show(String ... s) {
		System.out.println("String类型的可变个数形参show方法");
		for(int i = 0; i < s.length; i++) {//在可变个数形参的方法中也可以通过数组遍历输出
			System.out.print(s[i]+"\t");
		}
	}
}

理解变量的赋值

变量是基本数据类型,赋值的是变量所保存的数据值

变量是引用数据类型,赋值的是变量所保存的数据的地址值

值传递机制

方法的形参的传递机制:值传递

如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值

如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值

例:两数交换如果提取成一个方法,需要注意其具体地址里的内容是否发生变化,否则方法弹出后,两数值并未交换

封装性的引入

当我们创建对象后,可以通过“对象.属性”对对象的属性进行赋值。赋值操作会受属性的数据类型和存储范围约束之外。但实际中需要对属性添加额外限制条件,这个条件在属性声明时无法体现,于是将属性声明为私有的,通过方法进行限制条件的添加(setXxx)。针对于属性就体现了封装性。

封装性的体现(≠封装性):

我们将类的属性xxx私有化,提供公有的方法来获取(getXxx)和设置(setXxx)

构造器

定义构造器的格式:权限修饰符 类名(形参列表){

}

属性赋值的先后顺序

①默认初始化

②显式初始化

③构造器中初始化

④通过“对象.方法“或者“对象.属性”去赋值

JavaBean

JavaBean是一种Java语言写成的可重用组件

所谓JavaBean,是指符合如下标准的Java类:

1.类是公共的

2.有一个无参的公共的构造器

3.有属性,且有对应的get、set方法

UML类图

Java学习笔记——面向对象_第5张图片

this

this可以用来修饰、调用:属性、方法、构造器

理解为:当前对象或者当前正在创建的对象

1.this调用属性或方法:当构造器的形参或者是方法的形参与类的属性同名时,我们采用“this.变量”表示

2.this调用构造器:

​ 2.1我们在类的构造器中,可以显式的使用“this(形参列表)”方式,调用本类中指定的其它构造器

​ 2.2构造器中不能通过“this(形参列表)”调用自己

​ 2.3如果一个类中有n个构造器,则最多有n-1个构造器中使用了“this(形参列表)”

​ 2.4规定:“this(形参列表)”必须声明在当前构造器的首行

​ 2.5构造器内部,最多只能声明一个“this(形参列表)”,用来调用其他构造器

package

使用package声明类和接口所属的包,声明在源文件的首行

每“.”一次,表示一层文件目录

同一个包下,不能命名同名的接口、类

MVC设计模式

Java学习笔记——面向对象_第6张图片

import关键字

1.可以使用“xxx.*”的方式表示导入xxx包下的所有结构

2.如果使用的类或接口是java.lang包下或者本包下定义的,则可以省略import结构

3.如果在源文件中使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示

import java.util.Date;

Date date = new Date();
java.sql.Date date1 = new java.sql.Date(); 

4.使用“xxx.*”方式表明可以调用xxx包下的所有结构。但如果使用的是xxx子包下的结构,则仍需显式导入

import java.lang.reflect.Field;

Field field = new Field();

5.import static:导入指定类或接口中的静态结构

Others:

Java学习笔记——面向对象_第7张图片
两个println调用的方法不同,上面输出Object,下面输出char[]

继承

1.格式:

class A extends B{}

A:子类、派生类(subclass)

B:父类、超类、基类(superclass)

2.体现:

子类A继承父类B之后,子类A就获取了父类B中声明的所有属性和方法(父类中声明为private的属性和方法,子类继承之后,仍获取父类私有的结构,但由于其封装性,子类不能直接调用父类结构而已)

3.所有java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类

方法的重写override/overwrite

1.重写:子类继承父类之后,可以对父类中同名同参数的方法进行覆盖操作

2.规定:

//方法的声明
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
	//方法体
}

①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同

②子类重写方法的权限修饰符不小于父类的被重写的方法的权限修饰符

​ >特殊情况:子类不能重写父类中声明为private权限的方法(此时子类中的此方法不是重写的方法)

③返回值类型:

​ >父类被重写的方法的返回值类型为void,则子类重写的方法的返回值类型只能是void

​ >返回值类型是引用数据类型时,父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类型或者A类的子类

​ >返回值类型是基本数据类型时,子类重写的方法的返回值类型与父类被重写的方法的返回值类型一致。(比如父类为double,则子类也为double)

④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

⑤子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static(不是重写)

super关键字

1.super理解为父类的,可以用来调用:属性、方法、构造器

2.使用:

​ 2.1调用属性和方法:

​ 我们可以在子类的方法或者构造其中,通过使用”super.属性“或者“super.方法”显式调用父类中声明的属性或方法,通常省略super。但当子类中定义了与父类同名的属性或者子类重写了父类的方法之后,必须显式使用super。

​ 2.2调用构造器:

​ 2.2.1我们可以在子类的构造器中显式的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器

​ 2.2.2“super(形参列表)”的使用必须声明在子类构造器的首行!!!

​ 2.2.3类的构造器中,“this(形参列表)”和“super(形参列表)”只能二选一,不能同时使用

​ 2.2.4在构造器的首行,没有显示的声明this(形参列表)或super(形参列表),则默认调用的是父类中空参的构造器super()

​ 2.2.5在类的多个构造器中,至少有一个类的构造器中使用了super(形参列表)调用父类中的构造器

子类对象实例化的全过程

1.从结果上看(继承性):

​ 子类继承父类之后,就获取了父类中声明的属性和方法

​ 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性

2.从过程上看:

​ 当我们通过子类的构造器创建子类对象时,我们一定会直接或简洁的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才会看到内存中有父类中的结构,子类对象才可以考虑进行调用

3.明确:虽然创建子类对象时,调用了父类的构造器,但是从始至终就创建过一个对象,即为new的子类对象

多态性

1.理解多态性:一个事物的多种形态

2.何为多态性:

​ 对象的多态性:父类的引用指向子类的对象(或者子类的对象赋给父类的引用)

class Person{
	public void eat(){
        
    }
}

class Man extends Person{
	public void eat(){
        
    }
    
    public void walk(){
        
    }
}

public class PersonTest{
    public static void  main(String[] args){
        Person p1 = new Man();
        p1.eat();
        p1.walk();//错误,因为父类中没有这个方法
    }
}

3.多态的使用:虚拟方法调用

​ 有了对象的多态性之后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法

​ 总结:编译看左边 运行看右边

4.多态性使用的前提:①类的继承关系 ②方法的重写

5.对象的多态性只适用于方法,不适用于属性(属性编译和运行时都看左边)

class Person{
	int id = 1001;
}

class Man extends Person{
	int id = 1002;
}

public class PersonTest{
    public static void  main(String[] args){
		Person p = new Man();
        system.out.println(p.id);//结果是1001  id=1001和id=1002都在堆中,此时看左边
    }
}

6.举例

package a03;

public class Animals {

	public void eat() {
		System.out.println("动物进食");
	}
	public void shout() {
		System.out.println("动物叫");
	}
	
	public void func(Animals animals) {
		animals.eat();
		animals.shout();
	}
	
	public static void main(String[] args) {
		Animals a = new Animals();
		a.func(new Dog());
		a.func(new Cat());
	}
}

class Cat extends Animals{
	public void eat() {
		System.out.println("猫吃鱼");
	}
	public void shout() {
		System.out.println("喵喵喵");
	}
}

class Dog extends Animals{
	public void eat() {
		System.out.println("狗吃骨头");
	}
	public void shout() {
		System.out.println("汪汪汪");
	}
}

7.多态性是运行时行为

8.多态练习:调用属性和方法


class Base{
	int count = 10;
	
	public void display() {
		System.out.println(this.count);
	}
}

class Sub extends Base{
	int count = 20;
	
	public void display() {
		System.out.println(this.count);
	}
}


public class Test {
	public static void main(String[] args) {
		Sub sub = new Sub();
		System.out.println(sub.count);//20
		sub.display();//20
		
		Base base = sub;
		System.out.println(base.count);//10
		base.display();//20
	}
}

多态性的拓展——重写和重载

类内多态

方法的重载(一个相同一个不同)

要求:方法名相同,参数类型列表不同

  • 参数个数不同
  • 参数类型不同
  • 参数的顺序不同,和参数名称无关
  • 和范围无关、和返回类型无关、和抛出异常无关

方法重写——覆盖(三个相同)

  • 方法名称一致,参数类型列表必须一致,返回数据类型必须一致
  • 子方法的范围限定词要求大于等于父方法的范围限定词
  • 不允许抛出更多异常

Java学习笔记——面向对象_第8张图片

Java学习笔记——面向对象_第9张图片

重载:方法名相同参数不同 可以在一个类内,也可以在父子类之间

重写:方法签名一致 只能是父子类之间

类间多态

public class Test1 {
	public static void main(String[] args) {
		Animal animal = new Cat();
		animal.sleep();//正确 因为Cat从Animal中继承了方法
//		animal.eat() 语法错误
		/*任意一个对象有2个类型:编译期类型和运行时类型
		 * 对象a在编译期系统识别的类型是Animal类型,a没有eat(),所以编译错误
		 * 对象a在运行时,系统识别到它是Cat类型,所以执行睡觉方法时执行的是Cat的
		 * */
		//new谁就是谁
		//如果针对a对象需要调用Cat中特定的方法【父类中没有的方法】,则需要进行强制类型转换——窄化操作
	}
}

class Animal{
	public void sleep() {
		System.out.println("睡觉");
		
	}
}

class Cat extends Animal{
	public void sleep() {
		System.out.println("猫睡觉");
	}
	public void eat() {
		
	}
}

多态总结

三个定义两个方法

  • 三个定义:父类定义子类构建、接口定义实现类构建、抽象类定义实体类构建
  • 两个方法:方法重写和方法重载

多态可分为:

编译时多态:方法的重载

运行时多态:java运行时系统根据调用该方法的实例的类型来决定调用哪个 方法

向下转型

有了对象的多态性之后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能被调用。要调用子类特有的属性和方法就要向下转型:使用强制类型转换符

Person p = new Man();
Man m = (Man)p;
m.earnMoney();

使用向下转型时,可能会出现ClassCastException的异常。虽然编译时未报错,但运行时会出错。

Woman w = (Woman)pw.goShopping();

instanceof

1.a instance of A:判断对象a是否是类A的实例。如果是,返回true,如果不是,返回false

2.使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就向下转型,否则不转

3.如果a instanceof A返回true,则a instanceof B也返回true。其中类B是类A的父类

if(p instanceof Woman){    Woman w = (Woman)p;    w.goShopping();}

==和equals的区别

==:运算符

1.可以使用在基本数据类型变量和引用数据类型变量中

2.如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等(不一定类型要相同)

int i = 12;boolean j = falsesystem.out.println(i == j)//报错 boolean不能和其它基本数据类型进行比较

​ 如果比较的是引用数据类型变量,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体

equals()方法的使用:

1.是一个方法,而非运算符

2.只能是用于引用数据类型

3.Object类中equals()的定义:

public boolean equals(Objeat obj){    return(this == obj);}//说明Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体

4.像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用地址是否相同,而是比较两个对象的“实体内容”是否相同

String s1 = new String("hello");String s2 = new String("hello");system.out.println(s1==s2);//false  地址不同system.out.println(s1.equals(s2));//true 内容相同

5.重写equals(自动生成equals()方法)

通常情况下我们自定义的类如果使用equals()的话,也通常是比较两个对象的实体内容是否相同。那么,我们就需要对object类中的equals()进行重写

重写的原则:比较两个对象的实体内容是否相同

toString()

Object类中的toString()的使用

1.当我们输出一个对象的引用时,实际就是调用当前对象的toString()

2.Object类中的toString()的定义

public String toString() {        return getClass().getName() + "@" + Integer.toHexString(hashCode());    }

3.像String、Date、File、包装类都重写了Object类中的toString()方法

​ 使得在调用对象的toString()时,返回“实体内容”信息

4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”。(可以自动生成)

JUnit单元测试

步骤:

1.选中当前工程——右键选择——build path——add libraries——JUnit 4——下一步

2.创建Java类,进行单元测试

​ 此时的java类要求:①此类是public的 ②此类提供公共的无参构造器

3.此类中声明单元测试方法

​ 此时的单元测试方法:权限是public,没有返回值,没有形参

4.此单元测试方法上面需要添加注释@Test,并在单元测试类中导入:import org.junit.Test;

5.声明好单元测试方法之后,就可以在方法体内测试相关代码

6.写好代码之后,点击右键run as——JUnit Test

说明:

1.如果执行结果没有异常:绿条

2.如果执行结果有异常:红条

在实际开发过程中,只需要在相应代码前面加上@Test即可选择自动导包

包装类

1.java提供了8中基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征

2.掌握的:基本数据类型、包装类、String三者之间的相互转换
Java学习笔记——面向对象_第10张图片

基本数据类型与包装类的转换

基本数据类型——>包装类:调用包装类的构造器

int num = 10;Integer in1 = new Integer(num1);system.out.println(in1);
package a01;public class Test2 {	public static void main(String[] args) {		Person person = new Person();		System.out.println(person.isMale);//false		System.out.println(person.isFemale);//null	}}class Person{	boolean isMale;	Boolean isFemale;}

包装类——>基本数据类型:调用包装类的xxxValue()

Integer in1 = new Integer(12);int i1 = in1.intValue()system.out.println(i1+1);

JDK 5.0新特性:自动装箱和自动拆箱

自动装箱:基本数据类型——>包装类

自动拆箱:包装类——>基本数据类型

public class Test3 {	public static void main(String[] args) {		int num1 = 12;				Integer in1 = num1;//自动装箱				int num2 = in1;//自动拆箱		        boolean b1 = true;        Boolean b2 = b1;//自动装箱	}}

基本数据类型包装类与String的相互转换

基本数据类型、包装类——>String类:

方法一:连接运算(基本数据类型)

int num1 = 10;String s1 = num1+"";

方法二:调用String的valueOf(Xxx xxx);

int num1 = 12;String s1 = String.valueOf(num1);System.out.println(num1);		boolean b1 = false;String s2 = String.valueOf(b1);//String java.lang.String.valueOf(boolean b) 两个方法对应的形参不同System.out.println(s2);		Double d1 = 12.4;String s5 = String.valueOf(d1);//String java.lang.String.valueOf(Object obj)  两个方法对应的形参不同		

String类——>基本数据类型、包装类:

调用包装类的parseXxx(String s)

package a01;public class Test3 {	public static void main(String[] args) {		 String s1 = "123";		 String s2 = "123a";		 int i1 = Integer.parseInt(s1);//		 int i2 = Integer.parseInt(s2);		 System.out.println(i1);//123//		 System.out.println(i2);//NumberDormatExceotion		 		 String s3 = "true";		 boolean b1 = Boolean.parseBoolean(s3);		 System.out.println(b1);//true 		 String s4 = "true1";		 boolean b2 = Boolean.parseBoolean(s4);		 System.out.println(b2);//false (只要不是true就全是false)	}}

包装类面试题:

	@Test	public void Test() {		Integer i = new Integer(1);		Integer j = new Integer(1);		System.out.println(i==j);//false				Integer i1 = 1;		Integer j1 = 1;		System.out.println(i1==j1);//true				Integer i2 = 128;//相当于new了一个Integer对象		Integer j2 = 128;//相当于new了一个Integer对象		System.out.println(i2==j2);//false	}//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127之间,可以直接使用数组中的元素,不用再去new。//目的:提高效率

static关键字

1.static:静态的

2.static可以修饰:属性、方法、代码块、内部类(static不能修饰构造器、局部变量)

3.使用static修饰属性:静态变量(或类变量)

​ 3.1属性 按是否使用static修饰,又分为:静态属性vs非静态属性(实例变量)

​ 3.2static修饰属性的其他说明:

​ ①静态变量随着类的加载而加载,可以通过“类.静态变量”进行调用

​ ②静态变量的加载早于对象的创建

​ ③由于类只加载一次,则静态变量在内存中也会存在一份:存在在方法区的静态域中

​ 3.3举例:system.out Math.PI

4.使用static修饰方法:静态方法

​ 4.1随着类的加载而加载,可以通过“类.静态方法”进行调用

​ 4.2静态方法可以调用静态方法和属性

​ 非静态方法可以调用静态/非静态方法和属性

5.static注意点

​ 5.1在静态方法中,不能使用this关键字、super关键字(因为statics是归属于类的,在对象还未创建之前它就已经存在了)

​ 5.2静态属性和静态方法通过生命周期去考虑

6.开发中,属性或方法声明为static?

​ 6.1属性被多个对象共享

​ 6.2操作静态属性的方法设置为static 工具类中的方法,习惯上声明为static,比如:MAth、Arrays、Collections

7.static中的坑(反例之王)

  • 如果使用构造对象调用static方法,则使用谁声明就调用谁的静态方法
    Java学习笔记——面向对象_第11张图片

  • 如果子类中没有cc()方法,则调用父类中继承来的cc()方法,cc()方法中的pp()方法调用父类的(就近原则)

Java学习笔记——面向对象_第12张图片

Java学习笔记——面向对象_第13张图片

单例设计模式

1.所谓类的单例设计模式,就是采取一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例。

2.如何实现?

​ 饿汉式vs懒汉式

​ 饿汉式:对象加载时间过长 线程安全

​ 懒汉式:延迟对象的创建 目前写法线程不安全

//饿汉式
public class SingletonTest1 {
	public static void main(String[] args) {
		Bank bank1 = Bank.getInsatnce();
		Bank bank2 = Bank.getInsatnce();
		System.out.println(bank1==bank2);//true
	}
}

class Bank{
	
	//1.私有化类的构造器
	private Bank() {
		
	}
	//2.内部创建类的对象
	//4.要求此对象也必须声明为静态的
	private static Bank instance = new Bank();
	
	//3.提供公共的方法,返回类的对象
	public static Bank getInsatnce() {
		return instance;
		
	}
}
//懒汉式
public class SingletonTest1 {
	public static void main(String[] args) {
		Bank bank1 = Bank.getInsatnce();
		Bank bank2 = Bank.getInsatnce();
		System.out.println(bank1==bank2);//true
	}
}

class Bank{
	
	//1.私有化类的构造器
	private Bank() {
		
	}
	//2.内部创建类的对象
	//4.要求此对象也必须声明为静态的
	private static Bank instance = null;
	
	//3.提供公共的方法,返回类的对象
	public static Bank getInsatnce() {
		if(instance == null) {
			instance = new Bank();
		}
		return instance;
		
	}
}

类中代码块结构的作用

代码块(或初始化块)

1.代码块的作用:用来初始化类、对象

2.代码块如果有修饰的话,只能是static

3.分类:静态代码块vs非静态代码块

4.静态代码块

​ 4.1内部可以有输出语句

​ 4.2随着类的加载而执行,而且只执行一次

​ 4.3作用:初始化类的信息

​ 4.4如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行

​ 4.5静态代码块的执行优先于非静态代码块的执行

​ 4.6静态代码块内只能调用静态的属性、静态的方法

static String desc = "我是一个人";

static{
	desc = "我是一个爱学习的人"
}

5.非静态代码块

​ 5.1内部可以有输出语句

​ 5.2随着对象的创建而执行

​ 5.3每创建一个对象,就执行一次非静态代码块

​ 5.4作用:可以在创建对象时,对对象的属性等进行初始化

​ 5.5如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行

package a01;

public class Test3 {
	public static void main(String[] args) {
		String descString = Person.Desc;//调用Person类的静态属性时,静态代码块输出语句
	}
}

class Person{
	//属性
	String nameString;
	int age;
	static String Desc = "我是一个人";
	
	//构造器
	public Person() {
		super();
	}


	public Person(String nameString, int age) {
		super();
		this.nameString = nameString;
		this.age = age;
	}
	
	//代码块
	 
	static {
		System.out.println("静态代码块");
	}
	
	{
		System.out.println("非静态代码块");
	}
	//方法
	
	public void eat() {
		System.out.println("吃");
	}
	
	public static void sleep() {
		System.out.println("睡");
	}
}

代码块的练习:

package a01;

public class LeafTest {
	public static void main(String[] args) {
		new Leaf();
		System.out.println("*******");
		new Leaf();
	}
}

class Root{
	static {
		System.out.println("Root的静态初始化块");
	}
	{
		System.out.println("Root的普通初始化块");
	}//代码块先于构造器执行
	
	public Root() {
		super();
		System.out.println("Root的无参数构造器");
	}
	
}

class Mid extends Root{
	static {
		System.out.println("Mid的静态初始化块");
	}
	{
		System.out.println("Mid的普通初始化块");
	}
	public Mid() {
		super();
		System.out.println("Mid的无参数构造器");
	}
	public Mid(String msg) {
		this();
		System.out.println("Mid的带参数构造器:"+msg);
	}
}

class Leaf extends Mid{
	static {
		System.out.println("Leaf的静态初始化块");
	}
	{
		System.out.println("Leaf的普通初始化块");
	}
	public Leaf() {
		super("hello");
		System.out.println("Leaf的构造器");
	}

}

运行结果如下:

由父及子,静态先行

先父后子

静态的地位高于非静态的

静态属性和静态代码块同等地位,谁在前先执行谁

非静态属性和非静态代码块等地位,谁在前先执行谁

构造器最后执行

Java学习笔记——面向对象_第14张图片

package a01;

class Father{
	static {
		System.out.println("1111");
	}
	{
		System.out.println("2222");
	}
	public  Father() {
		System.out.println("3333");
	}
}

public class Son extends Father{
	static {
		System.out.println("4444");
	}
	{
		System.out.println("5555");
	}
	public  Son() {
		System.out.println("6666");
	}
	
	public static void main(String[] args) {
		System.out.println("7777");
		System.out.println("****");
		new Son();
	}
}

运行结果如下:

Java学习笔记——面向对象_第15张图片

属性赋值的先后顺序

①默认初始化

②显式初始化/在代码块中赋值

③构造器中初始化

④有了对象之后,通过“对象.属性”/“对象.方法”的方式进行赋值

class Person{
	int id = 4;
    {
        id = 5;
    }
    //结果为5
}

class Person{
    {
        id = 5;
    }
    int id = 4;
    //结果为4
}
//谁在后谁为结果

final

1.final可以用来修饰的结构:类、方法、变量

2.final用来修饰一个类:此类不能被其它类继承

​ 比如:String类、System类、StringBuffer类

final class A{
	
}

class B extends A{//The type B cannot subclass the final class A
	
}

3.final 用来修饰方法:表示该方法不可以被重写

​ 比如:Object类中getClass();

class A{	public final void name() {			}}class B extends A{	public void name() {	/*Multiple markers at this line	- Cannot override the final method 	 from A	- overrides a01.A.name*/	}}

4.final 用来修饰变量:此时的“变量”称为一个常量(此时要大写)

​ 4.1final修饰属性:可以考虑赋值的位置有:显式初始化、代码块初始化、构造器初始化

​ (不能在方法中对final属性赋值,因为当构造器结束时,加载了所有类的属性,应在这之前对final属性赋值)

public class Test{    final int WIDTH = 0;    final int LEFT;    final int RIGHT;}{    LEFT = 1;}public Test(){    RIGHT = 2;}public Test(int n){//无参构造器中给RIGHT赋值了,其他构造器中也要对RIGHT赋值。当有多个构造器时,无参构造器中的操作在有参中也要有    RIGHT = 2;}

​ 4.2final修饰局部变量:

​ 尤其是使用final修饰形参时,表明此形参是一个变量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。

public class Test{	    public void name(final int num){//      num++; ×  num为final,在方法体内,只能使用,不能重新赋值        system.out.println(num)    }}

5.static final 用来修饰属性:全局常量

​ 用来修饰方法:用类调用不能修改的方法

6.练习

public class Test1 {	public int add(final int num) {//		return ++num; 此处不能对num进行操作		return num+1;	}}

某个类的对象为final,但是可以对其属性进行修改

public class Test{    public static void main(String[] args){        Other o = new Other();        new Test().addOne(o);    }        public void addOne(final Other o){        o.i++;  //√ 虽然o为final,但可以对o中的属性进行操作//      o = new Other();  × 对o不能再进行操作    }}class Other{    public int i;}

抽象类和抽象方法的使用

abstract关键字的使用

1.abstract:抽象的

2.abstract可以用来修饰的结构:类、方法

3.abstract修饰类:抽象类

​ 3.1此类不能实例化(不能创建对象),只能通过子类去创建

​ 3.2抽象类中一定有构造器,便于子类实例化时调用(设计:子类对象实例化的全过程)

​ (抽象类不能实例化是因为语法规则,和有没有构造器没有关系)

​ 3.3开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

​ 具体应用中一般抽象类用于定义规范或者通用的方法

  • 通用方法是为了共享,避免子类重复定义
  • 规范是为严格要求子类必须提供实现

4.abstract修饰方法:抽象方法

​ 4.1抽象方法只有方法的声明,没有方法体

//不是抽象方法,是空方法体public abstract void pp(){}//是抽象方法public atstract void pp();

​ 4.2包含抽象方法的类一定是一个抽象类。反之,抽象类中可以没有抽象方法的

abstract class Person{//因为类中有抽象方法,此类必须声明为abstract	String name;	int age;		public Person() {			}		public abstract void name();	}

​ 4.3若子类重写了父类的所有抽象方法,此子类可以实例化

若子类没有重写了父类的所有抽象方法,此子类也是一个抽象类,需要用abstract修饰

5.抽象的应用场景:子类中重写父类的方法,父类方法只声明,没有具体的方法体内容时可以将父类方法设为abstract

6.abstract适用上的注意点:

​ 6.1abstract不能用来修饰:属性、构造器、代码块等结构

public abstract Animal();//×

​ 6.2abstract不能用来修饰私有方法、final方法、final类、静态方法(子类和父类中同名同参数的方法要么都声明为非static(考虑重写),要么都是static(此时不是重写))

​ (私有方法和final等都是不允许子类去重写的,但是abstract需要子类去重写从而实例化使用)

​ 6.3如果需要定义私有的构造器,则必须提供代餐的非私有的构造器

abstract class Parent{    private Parent(){}    public Parent(int age){        this();//此处可以调用无参构造器,也可以不调    }}class Son extends Parent{    public Son(){        super(10);//如果父类提供的是有参的构造器为公有,则需手动添加    }}

​ 6.4可以使用抽象类直接调用静态方法

创建抽象类的匿名子类对象

package a01;public class Test2 {	public static void main(String[] args) {                //非匿名类的非匿名对象		Student student = new Student();		name(student);		        //非匿名类的匿名对象		name(new Student());		        //匿名类 非匿名对象		Person person = new Person() {						@Override			public void walk() {				System.out.println("匿名类 非匿名对象的走路方法");			}						@Override			public void eat() {				System.out.println("匿名类 非匿名对象的吃饭方法");			}		};		name(person);		        //匿名类 匿名对象		name(new Person() {						@Override			public void walk() {				System.out.println("匿名类 匿名对象的走路方法");			}						@Override			public void eat() {				System.out.println("匿名类 匿名对象的吃饭方法");			}		});	}		public static void name(Person p) {		p.eat();		p.walk();	}}abstract class Person{	public abstract void eat();	public abstract void walk();}class Student extends Person{	public void eat() {		System.out.println("吃饭");	}	public void walk() {		System.out.println("走路");	}}

接口的定义和使用

Java学习笔记——面向对象_第16张图片

使用接口而不是使用具体的类,则可以实现在实现接口的多个具体实现类之间进行更换

1.接口使用interface来定义

2.Java中,接口和类是并列的两个结构。接口是一种特殊的类,所以命名不能和类冲突。外部接口的范围限定词只能使用public和默认package(只能包内可见)

3.如何定义接口:定义接口中的成员

​ 3.1 JDK7及以前:只能定义全局变量抽象方法

​ >全局变量:public static final

​ 全大写,下划线分词(书写时可以省略不写,但属性仍为此定义)

interface A{	String USER_NAMES="张三";}

​ >抽象方法:public abstract

​ 3.2 JDK8:除了定义全局变量和抽象方法之外,还可以定义静态方法、默认方法(接口中没有代码块这一说)

4.接口中不能定义构造器,意味着接口不能实例化。

public interface IA{    public IA(){} //Interfaces cannot have constructors}

5.Java开发中,接口通过类去实现(implements)的方式来使用

​ 如果实现类覆盖了接口中所有的抽象方法,则此实现类就可以实例化

​ 如果实现类没有覆盖了接口中所有的抽象方法,则此实现类仍为一个抽象类

package a02;public class InterfaceTest {	public static void main(String[] args) {		System.out.println(Flyable.MAX_SPEED);		Plane plane = new Plane();		plane.Fly();	}}interface Flyable{		//全局常量	public static final int MAX_SPEED = 7900;	int MIN_SPEED = 1;//public static final可以省略 		//抽象方法		public abstract void Fly();	void Stop();//public abstract可以忽略}class Plane implements Flyable{	@Override	public void Fly() {		System.out.println("驾驶员操作飞");			}	@Override	public void Stop() {		System.out.println("驾驶员操作减速停");			}	}abstract class Kite implements Flyable{//Kite类没有覆盖所有Flyable里的抽象方法,所以Kite类声明为abstract	@Override	public void Fly() {		System.out.println("风筝飞");			}	}

6.Java类中可以实现多个接口——>弥补了Java单继承的局限性

class AA extends BB implements CC,DD,EE{}

7.接口与接口之间可以继承,而且可以多继承

Java学习笔记——面向对象_第17张图片

8.接口的具体使用,体现多态性

即接口不能被实例化,只能通过实现类所实现,但是可以用于声明变量的类型

接口 变量名 = new 实现接口类();

9.接口,实际上可以看做是一种规范

10.开发中都是面向接口编程

public class UsbTest {	public static void main(String[] args) {				Computer computer = new Computer();		//1.创建了非匿名实现类的非匿名对象		Flash flash = new Flash();		computer.transferData(flash);				//2.创建非匿名实现类的匿名对象		computer.transferData(new Flash());				//3.创建匿名实现类的非匿名对象		USB phone = new USB() {						@Override			public void stop() {				// TODO Auto-generated method stub							}						@Override			public void start() {				// TODO Auto-generated method stub							}		};		//创建匿名实现类的匿名对象		computer.transferData(new USB() {						@Override			public void stop() {				// TODO Auto-generated method stub							}						@Override			public void start() {				// TODO Auto-generated method stub							}		});	}}class Computer{		public void transferData(USB usb) {//USB usb = new Flash() 体现多态性		usb.start();		usb.stop();	}}interface USB{		void start();	void stop();}class Flash implements USB{	@Override	public void start() {		System.out.println("flash开始");	}	@Override	public void stop() {		System.out.println("flash结束");	}}class Printer implements USB{	@Override	public void start() {		System.out.println("printer开始");	}	@Override	public void stop() {		System.out.println("printer结束");	}	}

接口应用

代理模式

工厂模式

练习

package a02;interface A{	int x = 1;}class B{	int x = 2;}public class C extends B implements A{		public void pX() {		//编译不通过//		System.out.println(x);The field x is ambiguous		System.out.println(super.x);//2		System.out.println(A.x);//1	}		public static void main(String[] args) {		new C().pX();	}}

Java8中接口的新特性

JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法

public interface CompareA{    //静态方法    public static void method1(){            }    //默认方法(范围限定词可以省略)    public default void method2(){            }}

1.接口中定义的静态方法不能被继承,只能通过接口来调用(不能new实现类的对象来调用接口的静态方法)

2.通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法

3.如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的默认方法。——>类优先原则

4.如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,如果子类没有重写此方法,则会报错,此时需要我们重写方法——>接口冲突

interface A{	default void pp() {		System.out.println("A");	};}interface B{	default void pp() {		System.out.println("B");	};}//如果不重写A/B中的pp方法会报错,因为pp方法的实现不唯一interface C extends A,B{	@Override	default void pp() {		A.super.pp();//调用A接口中的默认实现				B.super.pp();//调用B接口中的默认实现	}	}//如果不重写A/B中的pp方法会报错,因为pp方法的实现不唯一class D implements A,B{	@Override	public void pp() {		A.super.pp();//调用A接口中的默认实现				B.super.pp();//调用B接口中的默认实现	}	}

5.如何在子类(或实现类)的方法中调用父类、接口中被重写的方法

public void myMethod(){	method3();//调用自己定义的重写方法    super.method3();//调用父类中的声明的    CompareA.super.method3();    CompareB.super.method3();//调用接口中的方法}

6.声明类型是谁就只能调用谁中的方法
Java学习笔记——面向对象_第18张图片

必背:【接口和抽象类的异同点】

接口 抽象类
相同点 都是不断向上抽取而来的 都是不断向上抽取而来的
不同点 需要被实现,可以多实现 需要被继承,只能单继承
抽象方法、默认方法、静态方法 可以定义抽象方法和非抽象方法
接口的实现是like a关系 ,定义体系额外功能,子can do父 抽象类的继承是is a关系,定义该体系的基本共性内容,子 is a 父
只能定义常量,必须初始化 可以定义属性、常量,可以不初始化

内部类

用于看源码,实际开发很少用

1.Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

2.内部类的分类:成员内部类 (静态、非静态)vs 局部内部类(方法内、代码块内、构造器内)

class Person{    String name;        public void eat(){            }        //静态成员内部类    static class Dog{            }        //非静态成员内部类    class Bird{        String name;            }        //局部内部类    public void method(){        class AA{                    }    }        {        class BB{                    }    }        public Person(){        class CC{                    }    }}

3.成员内部类

​ ①作为外部类的成员

​ >在非静态的内部类中可以调用外部类的结构,哪怕是私有

​ >在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象

​ >可以被static修饰

​ >可以被4种不同的权限修饰:public、protected、缺省、private。外部类只能是public和缺省

​ >编译后有自己的独立字节码文件,只不过在内部类前面冠以外部类名和$符号

​ ②作为一个类

​ >类内可以定义属性、方法、构造器

​ >可以被final修饰,表示此类不能被继承。言外之意,不使用final就可以被继承

​ >可以被abstract修饰

4.关注的三个问题

4.1如何实例化成员内部类的对象

public class Test{    public static void main(String[] args){                //创建Dog实例(静态的成员内部类)        Person.Dog dog = new Person.Dog();                //创建Bird实例(非静态的成员内部类)        Person p = new Person();        Person.Bird bird = p.new Bird();//在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象    }}

4.2如何在成员内部类中区分调用外部类的结构

class Bird{    public void sing(){        Person.this.eat();//调用外部类的非静态属性    }        public void display(String name){        system.out.println(name);//形参name        system.out.println(this.name);//Bird里的name        system.out.println(Person.this.name);//Person的name    }}

5.补充:

  • 定义的内部类名称不能和外部类同名

Java学习笔记——面向对象_第19张图片

Java学习笔记——面向对象_第20张图片

Java学习笔记——面向对象_第21张图片

Java学习笔记——面向对象_第22张图片

Java学习笔记——面向对象_第23张图片

  • 非静态内部类不能写静态的成员(静态属性、静态内部类……),但可以继承静态的

Java学习笔记——面向对象_第24张图片

Java学习笔记——面向对象_第25张图片

Java学习笔记——面向对象_第26张图片

Java学习笔记——面向对象_第27张图片

Java学习笔记——面向对象_第28张图片

  • 最终类不论做什么修改都会引起地址的改变

Java学习笔记——面向对象_第29张图片

  • 匿名内部类不能定义静态成员

Java学习笔记——面向对象_第30张图片

最佳编程实践

  • 首先通过接口定义调用的规范
  • 然后使用抽象类实现接口,其中定义公共实现
  • 最后定义子类继承抽象类,实现所有的抽象方法

Tips[细节点补充]

  • Java中的两大原则:就近原则、最佳匹配原则

  • 类中声明了有参构造器之后,手动定义无参构造器

  • 浮点数不能进行等值判断,只能这样去判断Math.abs(this.re-app.re)<1e-6

Java学习笔记——面向对象_第31张图片

  • 引用和指针

指针是一个存放地址的变量,使程序员可以灵活地访问内存,由于对指针可以进行任意的运算操作,所以不安全。引用继承了指针节省内存的优点,但限制了对地址的操作。

  • 针对简单类型==比较的是数值

    针对引用类型==比较的是引用

  • 在字符中,0

Java学习笔记——面向对象_第32张图片

  • static里面不能使用super()/this()

    public static void rr(){	this.dd();//因为静态方法中可能还未创建对象,所以不能用this/super}
    
  • Object类中提供的toString方法会返回[类的全名@16进制hashcode值]

  • 属性的范围是整个类内,所以写在类中任何位置都可以

  • 父类private:能被继承,不能被访问

你可能感兴趣的:(Java学习,java,开发语言,后端)