面向对象
编程思想
面向过程:
关注每一步的细节
主导者从头到尾都是自己
核心功能不突出
更贴近计算器 机器执行的方式
优点:
简单直接,效率高
面向对象:
关注的是能够完成功能的对象
主导者只负责最终的功能实现,细节则交给对象来完成
不关注对象完成的过程,关注结果
核心功能突出
更贴近人类思考的方式
面向对象是基于面向过程的
优点:
效率略微差一点
应用场景:
对于大型项目来说,面向对象思想更适合,利于任务划分、分工合作、整体开发效率高
对于小型项目来说,面向过程思想更合适,更加的直接简单,开发效率较高
类和对象:
一切皆对象,万物皆对象
类:
将一类事物共有的特征和行为进行抽象从而得到的抽象概念
对象(实例):
类的具体化/实例化
定义类:
格式:
权限修饰符 class 类名{成员属性;成员方法;}
成员属性体现类的特征;变量
格式:
权限修饰符 数据类型 属性名 = 默认值;
成员方法体现类的行为;方法
格式:
权限修饰符 返回值数据类型 方法名(参数列表){}
案例:
//特征- 成员属性 成员变量
public String name;//姓名
public int age;//年龄
public char gendar;//性别
//行为 - 成员方法
public void eat(){//吃饭
System.out.println("吃饭");
}
public void sleep(){//睡觉
System.out.println("睡觉");
}
创建类的对象:
格式:
类名 对象名 = new 类{};
例如:
Person p = new Person();
调用对象的属性和方法:
对象.成员属性
对象.成员方法
非静态的方法都需要通过对象来调用
例如:
p.name="铁蛋";
p.age=18;
p.gendar='男';
System.out.println(p.name);
System.out.println(p.age);
System.out.println(p.gendar);
p.eat();
p.sleep();
对象的内存
每new一个对象就会在堆中开辟一个新的空间
堆中所有的属性必须要有值,如果没有明确指定 则给默认值
默认值取决于数据类型:
整形:0 浮点型:0.0 字符型:空字符 布尔型:false 引用数据类型:null
注意:
如果对象为null,对null调用了属性或者方法,程序运行会抛出异常NPE
通过调用对象的属性或者方法时,为了避免NPE异常,需要进行非努力了判断
成员变量、局部变量
成员变量:
定义类时 表示特征的 定义在类中方法外
局部变量:
定义在方法中或语句代码中的变量
成员变量和局部变量的区别:
1、定义位置不同
成员变量:类中方法外
局部变量:定义在方法中或语句代码块中
2、变量的作用域不同
成员变量:整个类中
局部变量:方法中或语句代码块中
3、内存储存位置不同
成员变量:堆内存中
局部变量:栈内存中
4、变量的生命周期
成员变量:随着对象的创建而创建,随着对象的释放而消亡
局部变量:随着方法以及语句代码块的执行而创建,随着方法执行完成而消亡
5、成员变量可以加权限修饰符来限制范围
局部变量不允许加权限修饰符
注意:
1、成员变量和局部变量是可以同名
2、调用时依据就近原则来执行
构造方法-Constructor
又称为构造函数、构造器
作用:
通过调用构造方法来创建对象
格式:
权限修饰符 类名(参数列表){方法体;}
注意:
1、构造方法没有返回值数据类型部分
2、构造方法和类名完全相同
分类:
无参构造
有参构造
注意:
1、如果在类中没有显示定义构造方法 ,则程序编译时会在底层自动生成一个无参构造方法;
2、如果在类中显示定义了构造方法,则程序不在默认提供
3、每创建一个对象就会调用一次构造方法
4、构造方法是可以重载的
this关键字
表示当前对象,this不是固定对象。而是动态变化
this便是哪一个对象取决于当前执行的代码是哪一个对象调用的
应用:
this.属性
例如:
在有参构造中进行初始化赋值
public Student(int sid,String name,int age,char gendar){
System.out.println("Student 有参构造");
//调用当前对象的属性
this.sid = sid;
this.name = name;
this.age = age;
this.gendar = gendar;
this.方法
如果在同一个类中,可以直接通过方法名来调用;但是等同于通过this.方法来调用
例如:
public void eat(){
//局部变量
String name = "xxx";
System.out.println(this.name+"吃饭");
//调用study方法
// study(); //底层也是默认通过this来调用的
this.study();
}
this(参数);-this语句 调用当前对象的其他构造方法
public Student(int sid,String name,int age,char gendar){
//调用另一个构造方法
this(sid,name);
this.age = age;
this.gendar = gendar;
}
//定义有参构造
public Student(int sid,String name){
this.sid = sid;
this.name = name;
}
注意:
1、this语句必须放在构造方法的首行
2、this语句可以互相调用构造方法 但是注意不要成环 编译会报错
构造代码块 局部代码块
Java中包含三种代码块:
构造代码块
局部代码块
静态代码块
构造代码块:
位置:
类中方法外
格式:
{代码块;}
执行:
在创建对象时执行,每创建一个对象执行一次构造代码块
执行顺序:
构造代码块在构造方法之前执行
作用:
构造代码块堆属性进行初始化赋值
提高代码复用性
局部代码块:
位置:
方法中 语句代码块中
格式:
{代码块;}
作用:
缩短局部变量的生命周期,提高内存的利用率
执行:
调用方法时,执行到局部代码块的位置开始执行
面向对象的三大特征
封装 继承 多态
封装:
体现:
类
成员属性
成员方法
私有化属性 提供公共的访问和设置的方法
理解:
相当于将所有的细节隐藏到一个盒子,只能看到盒子暴漏在外面的内容,而无法看到盒子里的内容
优点:
隐藏细节 安全性
提高了代码的复用性
提高了程序的可维护性
继承:
继承基于封装
概念:
Cat Dog类中的内容中存在大量重复的内容,可以将重复的内容提取到一个Animal类中,让Cat Dog继承Animal中的属性和方法直接继承过来,无需在写重复代码
提取出来的Animal类称之为父类(超类 基类)
Cat Dog称之为子类(派生类)
继承的格式:
父类只需要按照类定义即可
子类继承父类
public class 子类 extends 父类类名{}
extends关键表示继承关系
特点:
1、子类继承父类之后 可以将父类中的属性和方法都继承过来
父类中私有的属性和方法子类不可见
父类的构造方法不能被子类继承的
2、java中类时单继承,支持多重继承
单继承:
一个类只能继承一个类
缺点:
代码复用性略差
优点:
继承结构非常清晰,也不会出现冲突
多继承:
一个类可以继承多个父类
优点:
代码复用性较好
缺点:
继承可能会出现冲突
继承结构会非常混乱
多重继承:
class A extends B{}
class B extends C{}
class C{
public int num;
}
3、如果子类有特有的属性和方法,则在子类中单独定义就可以了
继承的优点:
提高代码了复用性
构建了程序的结构
多态:
多态基于封装和继承的
动态:
多种形态
分类:
编译时多态(已检查多态)
在编译期间体现出多种形态
体现:
方法重载
在编译期就可以根据实参的个数以及数据类型来判断调用哪一个重载的方法
运行时多态(未检查多态)
1、方法重写
2、向上造型
将子类对象赋值给父类类型的引用
向上造型+方法重写:
编译看左边 运行看右边
编译期间能否调用属性和方法取决父类类型中是否包含该属性和方法
运行期间则调用的是子类对象的属性和方法
优点:
提高了代码的复用性
提高了程序的灵活性和可扩展性
应用:
1、定义Teacher类 Teacher类包含Animal父类类型的属性,属性值都是子类对象
参考代码:
public class Teacher {
public String name;
public int age;
public Animal animal;
}
main:
Teacher teacher = new Teacher();
teacher.name = "王老师";
teacher.age = 40;
Cat cat = new Cat();
//设置cat的属性
cat.name = "小白";
cat.age = 2;
teacher.animal = cat;
System.out.println(teacher.name+"养了一只动物:"+teacher.animal.name);
teacher.animal.eat();
2、定义类 表示宠物商店Shop 父类类型做数组的数据类型 实际数组元素每一个都是子类对象
参考代码:
public class AnimalShop {
//特征
public String name;
//包含动物的属性
public Animal[] animals = new Animal[5];
}
main:
AnimalShopshop=newAnimalShop();
shop.name="爱宠物宠物店";
Cat cat = new Cat();
cat.name = "小白";
cat.age = 1;
shop.animals[0] = cat;//向上造型
Dog dog = new Dog();
dog.name = "旺财";
dog.age = 2;
shop.animals[2] = dog;
3、方法的返回值类型为父类类型 实际返回的是子类对象
方法的参数类型为父类类型 实际传入的是子类对象
参考代码:
//查询下标为i的动物
public Animal query(int i){
return animals[i];//animals[0]
}
//买动物
public void buy(Animal animal){//Animal animal = new Bird();
System.out.println("买了一只动物:"+animal.name+","+animal.age);
}
main:
Animal animal = shop.query(0); //Animal animal = new Cat();
System.out.println(animal.name+","+animal.age);
Bird bird = new Bird();
bird.name = "小花";
bird.age = 3;
shop.buy(bird);
方法重写: Override
子类继承父类继承了父类的方法,但是如果子类的方法不满足子类的需求,子类可以重新实现该方法,这就是重写
概念:
子类中存在和父类方法签名完全一致的方法
方法签名:方法名+参数列表
@Override 注解
作用是在编译时 检查当前方法是否满足方法重写。
特点:
子类重写了父类的方法,通过子类对象调用该方法,则调用的是子类重写之后的方法
父类的私有方法不能被重写的
方法重写的五大原则:
两等 两小 一大
两等
1、方法签名相等
2、方法的返回值
如果父类的返回值数据类型为void 或者基本数据类型,则要求子类的返回值数据类型必须和父类相同
一大:
3、权限修饰符
子类重写之后的方法的权限修饰符要求大于或等于父类方法的权限修饰符
两小:
4、方法的返回值
如果父类的返回值数据类型为引用数据类型,则要求子类的返回值数据类型必须小于或等于父类的返回值数据类型
5、方法的异常
子类抛出的异常类型要小于或等于父类抛出的异常类型
例如:
Animal类中定义了eat方法:
public void eat(){
System.out.println("吃~");
}
Cat类的重写父类的eat方法
public void eat(){
System.out.println("猫吃鱼")
}
1、方法重写的原则:
反证法
1、返回值数据类型
如果父类的返回值数据类型为引用数据类型,要求子类重写之后的方法的返回值类型要小于或等于父类方法的返回值类型
2、权限修饰符
子类的权限修饰符要大于或等于父类的权限修饰符
2、在向上造型前提下,调用子类所特有的属性和方法
方法重载和方法重写的区别:
方法重载:Overload
在同一个类中方法名相同参数列表不同
方法重写:Override
基于子父类继承关系,方法名相同参数列表也相同
权限修饰符
限制访问范围
分类:
public:公共的
protected:受保护的
默认:不写权限修饰符就是默认
private:私有的
作用:
public:本类、同包类、子类、全局
protected:本类、同包类、子类
默认:本类、同包类、同包子类
private:本类
实际开发中常用的是public private
修饰外部类:public 默认,不能用protected和private修饰
super关键字
表示父类对象
使用场景:
super.属性 - 调用父类对象的属性
super.方法 - 调用父类对象的方法
super(参数); - 调用父类的构造方法
super语句必须放在首行
this语句和super语句不能一起使用
创建子类对象会先创建父类对象 子类的构造方法中第一行默认会添加super(); 调用父类的无参构造 从而先创建了父类的对象;
instanceof 关键字 运算符
格式:
变量名 instanceof 类名
判断变量的实际类型是否是后边指定的类型
结果为boolean,如果是则为true 如果不是则为false
应用:
在强制类型转换之前,先进行类型的判断,从而避免出现异常
static静态
修饰属性、方法、代码块、内部类
静态属性:类属性 类成员
格式:
权限修饰符 static 数据类型 属性名;
例如:
public static int eyeNums = 2;
调用:
对象.静态属性名-----不建议
类.静态属性名-----建议 提高程序可读性
特点:
所有对象共享一个静态属性
一旦静态属性值发生改变,则所有对象的该属性都会随之发生改变
静态方法:类方法
格式:在方法定义时通过static关键字修饰即可
例如:
public static void main(String[] args)
调用:
对象.静态方法名() -- 不建议
类名.静态方法名() -- 建议 提高程序可读性
常见的静态方法:
Arrays.toString() Arrays.sort()
工具类中
注意:
1、静态方法中能不能定义静态变量?-不能
静态方法被调用时才能执行 其中的静态变量则要求在类加载时就要在静态区中创建空间
2、静态方法能不能直接调用非静态方法?- 不能
静态方法在类加载时就要加载的
非静态方法则是在创建对象时加载的
3、静态方法能否重载?-方法重载
4、静态方法能否被重写?- 不能
但是子类中允许存在和父类中完全一致的静态方法-普通的静态方法
静态代码块:
格式:
static{代码块;}
位置:
类中方法外
执行:
类调用静态属性
类调用静态方法
创建类的对象
特点:
只执行一次
应用场景:
配置文件的读取一般都放在静态代码块中
执行顺序问题:
1、静态代码块 构造代码块 构造方法
静态代码块-》构造代码块-》构造方法
2、普通属性 静态代码块 构造代码块 构造方法
先静态 后非静态
静态代码块-》普通属性-》构造代码块-》构造方法
3、静态属性 静态代码块 构造代码块 构造方法
先静态 后非静态
静态属性-》静态代码块-》构造代码块-》构造方法
子父类继承关系 创建子类对象会先创建父类对象
1、父类的构造代码块 父类的构造方法 子类的构造代码块 子类的构造方法
先父后子
父类的构造代码块-》父类的构造方法-》子类的构造代码块 -》子类的构造方法
2、父类的静态代码块 父类的构造代码块 父类的构造方法 子类的静态代码块 子类的构造代码块 子类的构造方法
先静态后非静态 先父后子
父类的静态代码块 -》子类的静态代码块-》父类的构造代码块-》父类的构造方法-》子类的构造代码块 -》子类的构造方法
静态的内存:
JDK1.8以前的版本:
Java的内存:
栈内存
堆内存
方法区:永久代实现的
静态区
JDK1.8及之后的版本:
Java的内存:
栈内存
堆内存
静态区
方法区:元空间实现的
内存图和之前的区别在于静态区到了堆内存
扩展:
JDK的内存结构 垃圾回收器的机制
JDK1.8的元空间
final最终的:
final关键字
final修饰属性、方法、类
常量-final修饰属性变量:
格式:
权限修饰符 final 数据类型 常量名 = 常量值;
例如:
public final int NUM = 0;
注意:
1、常量名要求所有字母必须全部大写 单词之间通过_分隔
2、如果常量直接定义,需要在定义时指定常量值,否则编译报错;
先声明常量属性,必须在对象创建完成之前完成常量赋值;
构造代码块中赋值 也可以在构造方法中赋值
3、常量一旦定义,值无法修改
基本数据类型:数据值不可变
引用数据类型:地址值不可变 但是地址指向的堆中的数据值可变的
final修饰方法-最终方法:
final修饰的方法不能被重写
提高程序的安全性和可维护性
final修饰类-最终类
final修饰的类不能被继承
提高程序的安全性和可维护性
例如:
String类被final修饰
static+final:
修饰属性:
静态常量
例如:
public static final int NUM = 8;
常见的静态常量:
public static final double PI = 3.14159265358979323846;
注意:
静态常量要么在定义时直接给出常量值;
要么必须在静态代码块中完成静态常量的赋值;
静态的特性:
类名直接调用
常量的特性:
值不可变
abstract-抽象类
abstract关键字,修饰类、方法
抽象类
abstract 修饰类
例如:
public abstract class Animal {}
抽象方法
abstract 修饰方法
例如:
public abstract void eat();
注意:
抽象方法不能包含方法体,在方法定义结束后一定要加;
抽象类的使用:
1、定义类继承抽象类,实现抽象类中所有的抽象方法
2、定义抽象类来继承抽象类,子抽象类会将父抽象类中所有的抽象方法以及非抽象方法都继承过来。子抽象类可以选择性的实现父抽象类中的抽象方法(可以全部实现 可以实现一部分 也可以都不实现)
特点:
1、包含抽象方法的类必须是抽象类;抽象类中可以没有抽象方法
2、抽象类中除了可以定义抽象方法之外,还可以定义 成员属性、成员方法、构造代码块、构造方法等都可以
3、抽象类不能直接通过new创建对象
4、抽象类的构造方法是提供给子类创建对象时调用的
5、抽象类支持向上造型 编译看左边运行看右边
Animal animal = new Cat();
6、abstract和private final static关键字不能一起使用修饰方法
abstract修饰的方法是抽象方法 抽象方法必须被重写
private final static修饰的方法则要求该方法不能被重写
7、abstract 和final不能一起修饰类
final修饰类不能被继承
abstract修饰类必须被继承
案例:
教练 运动员的案例
1、定义类表示篮球运动员
特征属性:
姓名 年龄 性别
行为方法:
吃饭 睡觉 打篮球
2、定义类表示乒乓球运动员
特征属性:
姓名 年龄 性别
行为方法:
吃饭 睡觉 打乒乓球
3、定义类表示篮球教练
特征属性:
姓名 年龄 性别
行为方法:
吃饭 睡觉 教打篮球
4、定义类表示乒乓球教练
特征属性:
姓名 年龄 性别
行为方法:
吃饭 睡觉 教打乒乓球
接口-interface implements
interface implements关键字
引出:
当类中需要额外扩展功能时,可以将方法放到接口中来实现
假设乒乓外交,需要一个讲英语的方法,而该方法放到父类中不合适,并不是所有的运动员、所有的教练都需要该方法,而是需要做外交的才需要。该方法放到每个具体的子类中也不合适,如果子类忘记写该方法了,需要调用时没有该方法。
此时最好将该方法定义到一个接口中。
接口中可以定义的内容:
1、只能定义公共的静态常量属性
默认所有属性都被public static final修饰
2、只能定义抽象方法
接口中所有方法默认都被public abstract修饰
3、JDK1.8提供了功能的扩展
允许接口中定义static或者default的方法,可以有方法体
static的静态方法:
例如:
static void method2(){
System.out.println("接口中的静态方法");
}
静态方法可以通过接口名直接调用,例如:Inter.method2();
注意:
静态方法只能通过当前接口直接调用 实现类无法获取静态方法
default的默认方法:
例如:
default void method3(){
System.out.println("接口中的默认方法");
}
注意:
默认方法不能通过接口调用,但是可以被实现类继承,所以通常通过实现类的对象来调用default的方法
接口的使用:
1、定义类实现接口,实现接口中的所有抽象方法
格式:
权限修饰符 class 类名 extends 父类 implements 接口名1,接口名2,…{}
例如:
public class InterImpl implements Inter{}
2、抽象类实现接口,可以选择性的实现接口中抽象方法
类和接口
类和类
继承
单继承
类和接口
实现
多实现
接口和接口
继承
多继承
注意:
1、接口不能new对象
2、接口中不能定义普通属性、普通方法、代码块、构造方法
3、一个类可以实现多个接口,要求实现每个接口中的抽象方法
4、类可以继承父类并实现接口,先继承再实现接口
重要 5、接口支持向上造型
接口名 对象名 = new 实现类();
抽象类和接口的区别:
1、抽象类是特殊类 接口不是类
2、一个类只能继承一个抽象类父类,但是可以实现多个接口
3、抽象类的继承extends关键字 接口的实现通过implements关键字
4、抽象类可以包含普通方法,而接口只能定义抽象方法
1.8之后接口中可以定义通过static default修饰的带方法体的方法
5、抽象类体现的是
is a
接口体现的是
like a
匿名对象
创建对象:
类名 对象名 = new 类名();
Person p = new Person();
匿名对象:
创建对象没有明确指定对象名
例如:
new Person();
new Person();
以上两个不同的匿名对象
应用:
1、匿名对象进行传参
2、匿名对象作为返回值来返回
3、匿名对象调用属性或方法
注意:
匿名对象只能使用一次
内部类
分类:
成员内部类
方法内部类
静态内部类
匿名内部类
成员内部类
位置:
类中方法外
例如:
//外部类
public class Outer {
//成员内部类
class Inner{}
}
注意:
1、成员内部类能被所有权限修饰符修饰
private修饰内部类 则只能在外部类内部使用
2、成员内部类也可以用final abstract修饰
3、成员内部类中可以定义普通属性、普通方法、构造代码块、构造方法等,但是不能包含静态的内容
4、外部类中可以直接new内部类的对象来调用内部类的属性和方法
5、在其他类中创建内部类的对象格式如下:
外部类.内部类 对象名 = new 外部类().new 内部类();
例如:
Outer.Inner inner = new Outer().new Inner();
6、内部类编译之后也会生成class字节码文件, 该文件名命名格式:
外部类类名$内部类类名
7、成员内部类可以访问外部类的属性和方法
方法内部类:
位置:
定义在方法中的类
例如:
public class Outer {
//方法
public void method(){
//定义方法内部类
class Inner{}
}
}
注意:
1、方法内部类不能加权限修饰符
2、方法内部类也可以用final abstract修饰
3、方法内部类中可以定义普通属性、普通方法、构造代码块、构造方法等,但是不能包含静态的内容
4、方法内部类只能在方法内使用
创建方法内部类的对象语句必须在内部类定义之后
Inner inner = new Inner();
6、方法内部类编译之后也会生成class字节码文件, 该文件名命名格式:
外部类类名$编号内部类类名
编号从1开始
7、方法内部类可以访问方法的局部变量以及调用其他方法
静态内部类
static关键字修饰成员内部类
位置:类中方法外
例如:
public class Outer {
//静态内部类
static class Inner{}
}
注意:
1、静态内部类能被所有权限修饰符修饰
private修饰内部类 则只能在外部类内部使用
2、静态内部类也可以用final abstract修饰
3、静态内部类中可以定义普通属性、普通方法、构造代码块、构造方法等,也可以包含静态的内容
4、外部类中可以直接new内部类的对象来调用内部类的属性和方法
5、在其他类中创建静态内部类的对象格式如下:
外部类.内部类 对象名 = new 外部类.内部类();
例如:
Outer.Inner inner = new Outer.Inner();
6、内部类编译之后也会生成class字节码文件, 该文件名命名格式:
外部类类名$内部类类名
同成员内部类一致
7、静态内部类可以访问外部类的静态属性和静态方法,注意的是不能直接访问非静态的内容
匿名内部类
特点:
只使用一次
格式一:抽象类 创建匿名内部类的对象
new 抽象类类名(){
实现抽象类中的抽象方法;
};
创建的是抽象类的子类的对象
匿名内部类:抽象类的子类
例如:
public abstract class Animal {
public abstract void eat();
}
Animal animal = new Animal(){
@Override
public void eat() {
System.out.println("猫吃鱼");
}
};
animal.eat();
格式二:接口 创建接口匿名内部类的对象
创建接口实现类的对象
匿名内部类:接口的实现类
new 接口名(){
实现接口中的所有抽象方法;
};
例如:
new Diplomacy(){
@Override
public void speakEnglish() {
System.out.println("乒乓球运动讲英语");
}
}.speakEnglish();
格式三:临时修改方法的实现
new 类名(){
将需要修改的方法重写
};
匿名内部类:指定类的子类
实际创建的是指定类的子类的对象
例如:
public class Person {
public void eat(){
System.out.println("吃。。");
}
}
new Person(){
@Override
public void eat() {
System.out.println("吃饭");
}
}.eat();
应用:
方法传参使用居多
public interface Diplomacy {
void speakEnglish();
}
//外交的方法
public static void wj(Diplomacy diplomacy){
diplomacy.speakEnglish();
}
wj(new Diplomacy() {
@Override
public void speakEnglish() {
System.out.println("讲英语");
}
});