整数类型: byte(1个字节)、short(2个字节)、int(4个字节)、long(8个字节,l/L)
浮点数类型: float(4个字节,f/F)、double(8个字节)
布尔类型: boolean(true/false)
字符类型: char(单引号,一个字符)
类class、接口interface、数组array、枚举enum、注解、字符串String
形参是基本数据类型,那么实参给形参的是数据值的副本,形参的修改不影响实参;
形参是引用数据类型,那么实参给形参的是地址值的副本,形参对象修改属性相当于实参对象修改属性
++在后,先加载值后自增;
++在前,先自增后加载值
1.更符合平时的思维习惯
2.把复杂的问题简单化
3.把我们从执行者变成指挥者
举例:
洗衣服:
面向过程编程思想:泡一泡->搓一搓->揉一揉->甩一甩->拧一拧->晾一晾
面向对象编程思想:买一台洗衣机->衣服放进去->启动[对象:洗衣机]
买电脑:
面向过程编程思想:打车去电脑店->选配置->和老板讨价还价->组装->打车带电脑回家
面向对象编程思想:打开某东->选配置->下单[对象:某东APP]
服务员点餐:面向对象思想的体现和应用
类:对一类事物的描述,是抽象的、概念上的定义。
对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)。
面向对象程序设计的重点是类的设计。
设计类,就是设计类的成员。
*先有类后有对象
例如:
类 ->对象
人类->霍金、爱迪生
汽车->奥迪、宝马
格式:
public class 类名{//类名:一类事物的名称
//在类中编写此事物的模板,对象就可以根据这个模板生产出来
属性;//描述信息
行为;//此类事物的功能[此类事物能做什么]
}
举例:
//描述生活中 人类 这类事物
public class demo1{
//成员:类中方法外的位置 叫 成员位置
//属性:成员变量
//成员变量定义可以不赋值直接使用
String name;
int age;
//行为:成员方法
//描述事物的成员方法,不加static关键字[main方法所在的类中定义方法加static,没有main方法的类中定义方法不加static]
public class eat(String food){
输出语句("姓名是:"+name+"的人在吃"+food)
}
public class sleep(){
输出语句("姓名是:"+name+",年龄是"+age+"岁的人在睡觉")
}
}
创建对象:
类名 对象名=new 类名();
如何使用对象:
对象名.属性:获取属性
对象名.属性=值; : 对属性进行赋值
返回值类型 变量名 = 对象名.方法(实参); : 调用对象的方式
使用:
public class demo2 {
public static void main(String[] args) {
demo1 d1=new demo1();
System.out.println("d1="+d1);
System.out.println("d1.name="+d1.name);
System.out.println("d1.age="+d1.age);
d1.name="林扬生";
d1.age=23;
System.out.println("d1.name="+d1.name);
System.out.println("d1.age="+d1.age);
d1.eat("面条");
d1.sleep();
System.out.println("-----------");
demo1 d2=new demo1();
System.out.println("d2="+d2);
System.out.println("d2.name="+d2.name);
System.out.println("d2.age="+d2.age);
}
}
* 被private修饰的成员只能在当前类中访问。
public 关键字:访问权限修饰符[公共的]->是java中最大的访问权限
private 关键字:访问权限修饰符[私有的]->是java中最小的访问权限
可以修饰:成员变量,成员方法,构造方法
效果:被peivate关键字修饰的 成员变量,成员方法,构造方法 只能在本类中使用
getter 方法:获取属性
public 属性的类型 getXxx(){//xxx属性的名称
return 属性名
}
setter 方法:设置属性
public 属性的类型 setXxx(){//xxx属性的名称
属性名
}
要求:只要编写事物描述类[描述一类事物的类],类中属性必须加上 `private` 关键字,并提供对应getter和setter方法。
变量访问的就近原则:
变量访问时会优先访问离自己最近的那个变量。
就近关系:
局部位置[方法内,方法形参上]<本类的成员位置[类中方法外的]<本类父类中的成员位置
打破变量访问的就近原则:
指定访问那里的变量
this:强制访问本类成员位置上的变量,方法
this.变量名:强制访问本类成员位置上的变量
this.方法名(实参):强制访问本类成员位置上的方法->永远不会写[所有的方法都是成员方法,没有局部方法]
关于使用:[重点]
this:强制访问本类成员位置上的变量,方法
this.变量名:强制访问本类成员位置上的变量
this.方法名(实参):强制访问本类成员位置上的方法->永远不会写[所有的方法都是成员方法,没有局部方法]
----------------------
关于概念:[理解]
this:当前对象的引用 [哪个对象调用含有this关键字的方法,哪个对象就是this此时指定的那个对象]
局部 和 成员 : 位置
局部位置:方法内,方法的形参上
成员位置:类中方法外
可以相同的是:局部变量名和成员变量名可以相同[因为成员变量和局部变量就不是同一种概念,所以JVM可以区分]
局部变量和成员变量的区别:
1.在类中的位置不同:
局部变量:方法内,方法的形参上。
成员变量:类中方法外。
2.作用域不同:[变量只在其定义的大括号内有效]
局部变量:只在其定义的方法内有效
成员变量:在整个类中都有效 [全局变量也是成员变量的另一种称呼]
3.在内存中不同:
局部变量:栈内存中
成员变量:堆内存中
4.初始化值不同:
局部变量:局部变量未赋值不能直接使用 [形式上局部变量除外]
成员变量:成员变量具有默认值 [参照堆内存对象内部数据默认值]
5.生命周期不同:
局部变量:随着方法的调用执行而产生,随着方法的弹栈而
成员变量:随着对象的创建而产生,随诊对象的被回收而死亡 [成员变量/成员方法都是属于对象]
6.IDEA中的颜色不同:[关乎使用的]->快速区分成员变量和局部变量
IDEA中未使用:灰色的
IDEA中如果使用了:
局部变量:黑色
成员便量:紫色
记忆法则:逻辑记
因为在类中位置不同,导致作用域不同和在内存中的位置不同。
因为在内存中位置不同,导致初始化不同和生命周期不同。
构造方法:Constructor
方法:方法就是方法,具备特定功能的代码块
构造:构造,创造
构造方法:用来构造和创建的方法->用来构造和创建对象的方法
创建对象的格式:
类名 对象名 = new 类名();
new 的作用:
1.创建对象
2.在堆内存中开辟内存空间
3.new 关键字 启动 构造方法
构造方法的定义格式:[重点]
//无形参的构造方法定义格式:无参构造
访问权限修饰符 类名(){
//方法体
}
//有形参的构造方法定义格式:带参构造
访问权限修饰符 类名(形参列表){
//方法体
}
构造方法的注意事项:
1.构造方法就是用来创建对象的
2.构造方法的启动要使用,关键字 new 来启动
3.当一个类中没有任何构造,JVM会赠送一个没有形参的构造方法[无参构造]
4.构造方法的方法名一定和类名一样[大小写都必须一样]//构造方法的方法名是大驼峰命名法
5.构造方法没有返回值类型版块//因为构造函数方法调用后的结果是固定的:生成了此类的对象
6.虽然带参构造可以在创建对象时给对属性赋值,但是不能省略setter方法;因为每调用一次构造方法就生成了一个新的对象,新的对象就不再是刚才那个对象了;[setter方法可以来修改属性值]
7.当在一个类中定义了任何构造方法,JVM默认赠送了无参构造就不送了[JVM就是舔狗]
8.构造方法也算是重载 [无参,带参构造]
步骤:
1.私有所有的属性 //描述事物的重点
2.编写无参构造
3.编写带参构造 [全参构造]
4.编写 每一个属性对应的 getter 和 setter方法
使用步骤:
1.创建对象
2.对象调方法
封装性:代码包裹起来
1.循环:把重复的代码包裹->提高代码的复用性
2.方法:把重复的功能用方法包裹->提高代码的复用性,实现了数据的传递
3.private:把事物的属性私有->提高代码的安全性[用getter和setter方法对属性进行赋值获取值]
4.定义事物描述类:把一类事物的属性和行为 包裹在类[class]里->提高代码的可用性
static 关键字能够修饰的是成员位置上的东西[成员变量,成员方法],被修饰的成员叫做静态成员。
static关键字特点:
1.被static关键字修饰的成员被所有对象 共享[一变都变]
2.被static关键字修饰的成员不属于单个对象,属于类[static修饰的成员]
3.被static关键字修饰的成员可以使用类名直接调用[使用方式]
4.被static关键字修饰的成员随着类的加载而加载优先于对象[生命周期]
静态成员随着类的加载而加载,优先于对象存在
//Student
public class Student {
String name;
static String schoolName;
public Student() {
}
public Student(String name, String schoolName) {
this.name = name;
this.schoolName = schoolName;
}
public void getInfo(){
System.out.println(this.name+"-"+this.schoolName);
}
}
//TestDemo
public class TestDemo {
public static void main(String[] args) {
Student student1=new Student("迪丽热巴","北大");
Student student2=new Student("杨幂","清华");
student1.getInfo();
student2.getInfo();
Student.schoolName="哈佛";
student1.getInfo();
student2.getInfo();
}
}
非静态成员变量 | 非静态成员方法 | 静态成员变量 | 静态成员方法 | |
---|---|---|---|---|
非静态成员方法 | √ | √ | √ | √ |
静态成员方法 | × | × | √ | √ |
1.方法重载与静态无关。
2.静态方法成员只能访问静态成员变量和静态成员方法[静态只能访问静态]。
3.静态成员加载的时间更早,非静态成员加载晚。
4.静态方法内访问非静态成员:创建非静态成员的对象。
5.静态方法内是不可以使用this关键字,this关键字是对象的引用,而非静态方法可以使用类名直接调用,当类名调用方法时,对象还没有产生this就没有指向。
static 的使用标准:
1.main方法所在的类定义方法/定义成员变量,都会加上static[静态只能访问静态]
2.事物描述类中的方法和成员变量 一般情况下是不加static关键字,因为就是希望这些方法和属性被对象调用。
static 的使用场景:工具类->提供快捷功能的类[Arrays]
Arrays:关于数组操作的工具类型
Arrays.toString(任意数组对象):漂亮打印数组
Arrays.sort(任意数组对象):数组排序[底层逻辑快排]
Math:数学工具类
static double PI
static int abs(int a):求a的绝对值
static double sqrt(double a):求a的平方根
static double cbrt(double a):求a的立方根
static double ceil(double a):向上取整
static double floor(double a):向下取整
static long round(double a):四舍五入
static int max(int a,int b):求a和b的最大值
static int min(int a,int b):求a和b的最小值
static double pow(double a,double b):求a的b次方幂
static double random():求随机数[0.0,1.0)
自己编写工具类:
1.定义类 类名:XxxUtil->Xxx:需要完成的功能
2.私有无参构造,并不提供任何其他构造方法->不让别人创建对象[直接用类名调用成员]
3.把类中所有的成员变量和成员方法都用 static 修饰
继承性:Java中类与类之间的联系
//Java中类与类之间的关系:父子关系,没关系
Java中继承关系的特点:
1.单一继承,多层继承:一个子类只能有一个父类,父类也可以有自己的父类//儿子只有一个亲爹,爹也可以有自己的爹
2.一个父类可以有多个子类//一个爸爸可以有多个孩子
3.Java中所有的类[包含数组]直接或间接继承于Object:object 类是所有类的直接或间接的父类//如果一个类没有写继承关系,默认继承 Object
4.子类继承父类,可以使用父类中所有非私有的成员变量和成员方法->父爱如山//父类的私有成员子类不可以访问->父亲的小秘密,儿子不知道
5.父类 不可以使用 子类成员 [父类只能使用父类自己的或者父类的成员] ->父亲是渣男:父亲并不知道自己有这个孩子
6.子类/子类对象 的功能多一些 [使用自己的还能使用自己父类的]
7.子类 要加载优先于 父类加载 -> 先有爸爸再有儿子
继承的关键字:extends
继承的格式:
* [注意] 多个类写在一个Java文件中:前提->只能有一个类被public修饰,且这个类必须和Java文件的名称一致
//定义爷类
public class Grandpa{
}
//定义父类
public class Father extends Grandpa{
}
//定义子类
public class Son1 extends Father{
}
public class Son2 extends Father{
}
成员变量:就近原则
成员方法:具体 new 的是什么类型的对象,那么就调用哪个类中的的方法[动态绑定]
构造方法:子类对象要加载优先于加载父类
继承成员变量的特点:
1.在父类中访问:只能访问父类自己的和自己父类非私有的
2.在子类中访问:只能访问子类自己和父类非私有的[如果父类有,子类也有,优先访问子类自己的]
语法解释:变量访问的就近原则 [方法局部的 < 本类成员的 < 本类父类的 ]
3.在第三方类中创建父类对象访问:只能访问自己和父类的
4.在第三方类中创建子类对象访问:可以访问自己的和父类非私有的,变量访问就近原则。
//例子
public class Demo {
public static void main(String[] args) {
Father father =new Father();
System.out.println(father.nameFather);
System.out.println(father.numFather);
father.show();
//System.out.println(father.numSon);//无法访问子类
Son son=new Son();
System.out.println(son.numSon);
System.out.println(son.nameSon);
System.out.println(son.nameFather);//可以访问父类
System.out.println(son.numFather);
son.show();
}
}
class Father{
int numFather=100;
String nameFather="张三";
public void show(){
System.out.println(numFather);
System.out.println(nameFather);
}
}
class Son extends Father{
int numSon=200;
String nameSon="李四";
public void show(){
int numSon=300;
System.out.println(numSon);
System.out.println(this.numSon);//访问本类的
System.out.println(super.numFather);//访问父类的成员变量
System.out.println(nameSon);
}
}
* 构造方法的注意事项:
* 1.在每一个构造方法的第一句代码中 隐藏着super()[启动父类的无参构造]
* 2.在构造方法内调用了其他构造方法的语句,必须是代码的第一行
* 3.除了super()这样的语句外,还有
* super():调用父类的无参构造[默认赠送的]
* super(实际参数):调用父类的带参构造
* this():调用本类的无参构造
* this(实际参数):调用本类的带参构造
* 4.在构造方法内只能访问本类的构造
* 5.如果在构造方法内调用了其他的构造方法 默认赠送的super()就不送了
* 6.在子类的构造方法内 必须要有个构造方法去加载父类
方法的重载:overload
要求:
1.同一个类
2.方法名必须相同
3.形参列表不同
4.与返回值不同,形参变量无关,方法体无关,访问权限修饰符无关
重载的使用:用的多,定义的少[重载特性是方便调用者]
方法的重写:
要求:
1.必须有继承关系 / 实现关系[接口]
2.子类重写的父类的方法,要求子类的的声明和父类方法声明完全一致[方法的声明{}]
3.子类重写后的方法体可以不一样
重写的使用:用的多,定义的少
使用场景:当父类方法不能够满足子类该类行为的要素,ya可以在子类中重写父类的方法,修改父类方法的方法体
重写的注意事项:
1.重写方法不一样的环节:1%
//public > protected > 默认不写 > private
a.访问权限修饰符:子类方法的访问权限修饰符 >= 父类方法的访问修饰符
b.形参变量名可以不一样
c.返回值类型:子类重写方法的返回值类型可以不一样
Object > Number > Integer
Object > String
I.void:必须一样
II.基本数据类型 :必须一样
III.引用数据类型:子类方法的返回值类型 必须是 父类返回值类型的 子类或者同类
2.@Override注解:
放在子类的重写方法的声明上可以校验子类重写方法的格式时候正确//只要没有报红就是方法重写写对了
写方法重写时,必须加上@Override注解
3.
静态方法没有重写概念;why?:静态属于类,在哪个类中就是哪个类的成员
概念上:[理解]
this : 当前对象的引用[哪个对象启动带有this关键字的方法,此时方法内的this就代表这个对象]
super : 就是一个关键字 代表 父类的XXX
编写代码时:
this : 打破局部位置约束,强制访问本类成员位置上的 变量和方法 / 构造方法
super : 打破本类的位置约束,强制访问本类父类成员位置上的 变量和方法 / 构造方法
this.变量名 : 强制访问本类成员位置上的变量 [打破局部位置的访问的就近原则]
this.方法名() : 强制调用本类成员位置上的无参方法->不会用:所有的方法都是成员方法
this.方法名(实参) : 强制访问本类成员位置上的带参方法->不会用:所有的方法都是成员方法
super.变量名 : 强制访问本类父类成员位置上的变量[打破本类成员位置的访问就近原则]
super.方法名() : 强制调用本类父类成员位置上的无参方法->会用:因为方法有重写[子父类可以出同名方法,要在子类中访问父类的同名方法]
super.方法名(实参) : 强制调用本类父类成员位置上的带参方法->会用:因为方法有重写[子父类可以出同名方法,要在子类中访问父类的同名方法]
this() : 调用本类的无参构造
this(实参) : 调用本类的带参构造
super() : 调用本类父类的无参构造[所有构造的默认第一句]
super(实参) : 调用本类父类的有参构造
继承的好处:
//抽离的思想:可以把子类的共性[属性,行为]进行向上[往父类]抽取,就可以把共性写在父类中,子类就可以不写了,只需子类继承父类
1.提高代码复用性 [从子类往父类看]
2.对子类功能进行拓展/对子类功能进行约束 [从父类往子类看]
继承的弊端:
提高了代码的耦合性[耦合性:依赖关系 内聚性:独立完成功能的能力]
//写代码的大原则:高内聚低耦合
继承特性:利大于弊
类 : 一类事物的抽象 [描述的是一个范围]
抽象类 : 比类更抽象的类 [在抽象类中可能会有 抽象方法] -> 被 abstract 修饰的类 就是抽象类
格式 :
public abstract class 类名{
}
抽象方法 : 被 abstract 修饰的方法
特点 : 抽象方法没有方法体
格式 : 权限修饰符 返回值类型 方法名(形式参数列表);
抽象类 : 抽象类中可以什么都没有,只要被abstract修饰的类就是抽象类!
1. 成员变量 : 可以有,可以有多个
2. 构造方法 : 可以有,可以有多个
3. 成员方法 : 可以有,可以有多个
4. 静态成员方法 : 可以有,可以有多个
5. 抽象方法 : 可以有,可以有多个 [抽象方法必须存在于抽象类或者接口中]
6. 自定义常量
抽象类 虽然有构造方法 但是抽象类不可以创建对象!
//为什么抽象类不能创建对象 :
抽象类的使用步骤 :
1. 创建一个此抽象类的子类 [让子类继承抽象父类]
//手段1 : 解决了父类的抽象方法
2. 让子类重写抽象父类中所有的抽象方法 // 抽象方法不可以存在于普通类中
3. 在测试类中创建子类对象,使用子类对象调用方法
or
//手段2 : 把父类的抽象方法延续到了子类的子类去解决
2. 让子类自己变成抽象类 // 为了解决抽象方法的存活问题
3. 让子类的子类去解决父类中的抽象方法
....
抽象类 : 一般都是作为父类存在[抽象类也可以是子类],抽象父类中的抽象方法可以 "强制" 约束 子类的行为; [子类必须重写父类的抽象方法或者子类自己变成抽象类]
final 关键字 : adj. 修饰符 -> 最终的
类 : 最终的类
被final修饰的类没有子类[不能被继承]
成员变量 : 最终的成员变量
[注意]被 final 修饰的变量必须赋值,可以在代码块或构造函数中进行赋值,但是只能赋值一次,若赋值第二次将会报错。
被final修饰的变量不能改变 [变常量]
被final修饰的成员变量一般都作为 : 自定义常量
自定义常量的标准定义格式 :
public static final 数据类型 常量名 = 初始化值;//必须给初始化值
常量名的命名 : 全部大写 多个单词之间使用 下划线 分隔
成员方法 : 最终的成员方法
被final修饰的成员方法不能被重写
被final修饰的成员方法可以重载 [重载并不是方法的延续,而是另一个新方法!]
局部变量 : 最终的局部变量
被final修饰的变量不能改变 [变常量] -> 局部位置的常量不属于自定义常量[值不能被改变了]
//实时final特性 : 当局部变量在匿名内部类中使用时,会实时的加上final关键字
public class Demo {
//被 final 修饰的变量必须赋值且只能被赋值一次
final int age;
final String name;
//代码块中
{
age = 20;
}
//构造器中
public Demo() {
name = "张三";
}
public Demo(String n) {
name = n;
}
}
接口[重点] : Java 引用数据类型五大类[类 class,数组 Array , 接口 interface , 枚举 enum , 注解 @interface [Annotation]]中的一类;
类 : 一类事物的抽象
抽象类 : 比类更抽象的类
接口 : 比抽象类更抽象的"类"
接口的关键字 : interface
生活中的接口 和 代码中的接口 是一个意思吗 ?
生活中的接口和代码中的接口沿用的是相同的思想! [USB接口,网线接口,充电接口,API(应用程序开发接口)]
简单理解接口 : 用来连接的口子 [不是接口的思想]
接口的思想 : 提供规范 [规范非常的重要] 如果规范了后期的使用就会非常的方便
提供规范的要求 : 公开 [public]
格式 :
//接口名的命名方式 和 类名 一致的 : 大驼峰 [见名知意]
//定义一个接口 也是定义了一种引用数据类型
public interface 接口名{
}
类和类之间的关系 : 没关系 , 继承关系 [单一继承,多层继承 -> extends]
类和抽象类之间的关系 : 没关系 , 继承关系 [单一继承,多层继承 -> extends]
抽象类和抽象类之间的关系 : 没关系 , 继承关系 [单一继承,多层继承 -> extends]
接口和类[抽象类]之间的关系 : 没关系 , 实现关系 [多实现(一个类可以实现多个父接口[叔叔]) -> implements]
//一个子类[实现类]实现多个父接口
public class Zi implements Uncle1,Uncle2...{
}
//一个子类[实现类]继承一个父类实现多个父接口
//一个子类继承了一个父类实现了2个父接口
public class Zi extends Fu implements Uncle1,Uncle2{
}
接口和接口之间的关系 : 没关系 , 继承关系 [多继承 -> extends]
//一个子接口 继承 2个父接口
public interface InterZi extends InterFu1,InterFu2{
}
总结一下 :
1 . 只有接口和类之间是 实现关系 [多实现] 其他的全都是继承 [接口和接口之间是多继承]
8之前:
成员变量,抽象方法
JDK8之前的接口的成员 :
1. 自定义常量 : 所有的成员变量都是自定义常量
//接口中的成员变量 都默认被 public static final 修饰
public static final int NUM = 100;
2. 抽象方法 : 可以有,可以有多个 [抽象方法必须存在于抽象类或者接口中]
//接口中的成员方法都默认被 public abstract 修饰
public abstract void show();
public abstract void show1();
如何使用 : 接口不可以直接创建对象
1. 准备一个类实现接口
2. 在实现类中重写父接口中所有的抽象方法
3. 在测试类[在使用接口的地方]中,创建的是实现类的对象,并用是实现类的对象调用重写后的抽象方法
or
2. 把实现类变成抽象类,交付给实现类的子类重写父接口中的抽象方法
....
//高版本兼容低版本的所有内容
1. 自定义常量 : 所有的成员变量都是自定义常量
2. 抽象方法 : 可以有,可以有多个 [抽象方法必须存在于抽象类或者接口中]
//新增
3. 默认方法 :
格式 :
public default 返回值类型 方法名(形式参数列表){
方法体;
}
默认方法默认被 public 修饰
使用 : 实现类可以选择性的重写 父接口中的 默认方法,使用实现类对象去调用!
注意事项 :
当实现类实现的多个父接口中有相同的默认方法,作为实现类必须重写这个相同的默认方法
//新增
4. 静态方法 :
格式 :
public static 返回值类型 方法名(形式参数列表){
方法体;
}
静态方法 默认被 public 修饰
使用 : 接口名直接调用! [静态方法没有重写概念] //接口中的静态方法只能用接口名调用!不能用实现类对象调用
//高版本兼容低版本的所有内容
1. 自定义常量 : 所有的成员变量都是自定义常量
2. 抽象方法 : 可以有,可以有多个 [抽象方法必须存在于抽象类或者接口中]
3. 默认方法 :
4. 静态方法 :
新增 :
5. 私有方法 : 为默认方法服务
格式 :
private 返回值类型 方法名(形参列表){
方法体;
}
私有方法没有默认的修饰符
使用 : 实现类看不到父接口的私有方法,接口中的私有方法只能为接口内部方法[默认方法和静态方法]服务
6. 静态私有方法 : 为静态方法服务
格式 :
private static 返回值类型 方法名(形参列表){
方法体;
}
静态私有方法没有默认的修饰符
如果一个类中全都是抽象方法,就 没必要定义抽象类,而是直接定义接口!
多态性 : 事物的多种形态
举例 :
人 -> 白人,黄种人,黑人,红种人
人 -> 男人,女人
狗 -> 哈士奇,金毛...
多态的前提 :
1. 多态必须有继承或者实现关系
2. 有方法的重写 -> 体现多态使用的特性 : 动态绑定
3. 必须 父引用指向子类对象 / 子类引用强制接收父类引用
Fu fu = new Zi(); / Zi zi = (Zi)fu;
[注意!]多态使用的情况下,编译时看等号左边,运行时才看等号右边。
多态的本质 : Java 引用数据类型子父类/实现类和父接口 之间的类型转换问题
基本数据类型之间有类型转换 :
前提 : double > float > long > int > byte,short,char [boolean 不参与]
自动提升 : 大类型的变量直接接收小类型的值/变量
强制转换 : 小类型的变量强制接收大类型的值/变量
引用数据类型之间的类型转换 :
前提 : 父类和子类/父接口和实现类
向上转型 : Fu fu = new Zi(); //Animal a = new Dog();
向下转型 : Zi zi = (Zi)fu;// Dog d = (Dog)a;
[注意]成员变量、静态方法看左边,非静态方法:编译看左边,运行看右边。
==》简单的理解意思就是.比如Animal a = new Cat(); 定义了一个Animal类型的引用,指向新建的Cat类型的对象.
这个时候他的成员变量和静态方法与父类是一致的,他的非静态方法,在编译时是与父类一致的,运行时却与子类一致(发生了重写)。
//多态有继承或者实现的前提条件,所以成员访问的特点和继承关系下成员访问的特点一致!
成员变量 : 就近原则 【局部位置 《 本类成员位置 《 本类父类成员位置】
强制访问本类成员 : this.变量名
强制访问本类父类成员 : super.变量名
成员方法 : 具体 new 的是什么类型的对象,那么就调用哪个类中的方法 【看等号右边的类型】-> 动态绑定
构造方法 : 子类对象要加载优先加载父类
多态的弊端 : 父引用不可以访问子类的特有成员;
如何解决多态的弊端 :
向下转型,让父引用知道自己具体是什么类型的对象!
多态向下转型的弊端 : ClassCastException [类型转换异常]
把不是此类的对象转换成了此类型对象 [不允许出现 -> Java是强类型语言]
如何解决向下转型的弊端 : 提前做判断,判断后再转换 [!!!]
格式 : 对象名 instanceof 类名
作用 : 判断前面的对象是否属于后面的类型的 -> 属于返回true 不属于返回false
我们不会主动的以多态的形式创建对象,因为父引用看不到子类的特有行为,后续还要向下转型!
我们要创建对象就一定创建子类对象,子类对象的功能最多 !
使用场景 : n -> 此类型的子类型们 , 1 -> 父类型自己[父类型自己能创建对象]
1. 把父类型作为方法的形参,那么启动方法传入的参数类型有 n + 1 种
2. 把父类型作为方法的返回值类型,那么返回方法结果的数据类型有 n + 1 种
3. 把父类型作为容器的元素数据类型,那么容器内可以存储的元素类型 有 n + 1 种
4. 异常体系中使用 ...
//示例
public class demo {
public static void main(String[] args) {
Man man1 = new Man();
enter(man1);
Woman woman1 = new Woman();
enter(woman1);
Person man2 = new Man();
man2.dancing();
//man2.sport();//编译时要看等号左边,运行时才看等号右边,所以编译不通过,通过向下转型来实现
show(man2);//
Person woman2 = new Woman();
woman2.dancing();
//woman2.shop();//编译时要看等号左边,运行时才看等号右边,所以编译不通过,通过向下转型来实现
show(woman2);
}
//动态绑定
//Person person=new Man() / new Woman()
public static void enter(Person person) {
person.dancing();
}
//向下转型 / 动态绑定
public static void show(Person person){
//提前判断:a instanceof b [对象a是否属于b类型]
if(person instanceof Man){
Man man=(Man) person;
man.sport();
}else if(person instanceof Woman){
Woman woman = (Woman) person;
woman.shop();
}
}
}
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void dancing() {
System.out.println("跳舞");
}
}
class Man extends Person {
@Override
public void dancing() {
System.out.println("男人跳街舞");
}
public void sport(){
System.out.println("男生运动");
}
}
class Woman extends Person {
@Override
public void dancing() {
System.out.println("女人跳芭蕾");
}
public void shop(){
System.out.println("女人购物");
}
}
枚举 : 一组常量的集合 [属于一种特殊的类,里面包含一组有限的特定的对象]
使用:
1.自定义类实现枚举
(1)将构造器私有化,防止 new
(2)去除setXxx相关方法,防止属性被修改 [只能读不能写]
(3)在类内部,直接创建固定的对象
2.使用 enum 关键字实现枚举
(1)使用关键字 enum 替代 class
(2)将构造器私有化,防止 new
(3)去除setXxx相关方法,防止属性被修改 [只能读不能写]
(4)直接使用 : 常量名(实参列表)
(5)将常量放在最前面[开头,否则报错]
(6)多个常量之间使用逗号","来间隔 [不能使用分号]
//1.自定义类实现枚举
public class Demo1 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
System.out.println(Season.AUTUMN);
System.out.println(Season.WINTER);
}
}
class Season{
private String name;
private String desc;
public static final Season SPRING =new Season("春天","温暖");
public static final Season SUMMER =new Season("夏天","炎热");
public static final Season AUTUMN =new Season("秋天","凉爽");
public static final Season WINTER =new Season("冬天","寒冷");
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
//2.使用 enum 关键字实现枚举
public class Demo2 {
public static void main(String[] args) {
System.out.println(Season.SPRING);
System.out.println(Season.SUMMER);
System.out.println(Season.AUTUMN);
System.out.println(Season.WINTER);
}
}
enum Season {
SPRING("春天", "温暖"),
SUMMER("夏天","炎热"),
AUTUMN("秋天","凉爽"),
WINTER("冬天","寒冷");
private String name;
private String desc;
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
可变参数 : 形参的一种定义格式,按照可变参数的格式定义形参,那么调用方法时可以传入任意个此类型的实参
格式 :
数据类型...变量名 -> 形参的位置上写
可变参数的本质是数组!!
注意事项 :
1. 在可变参数的后方不可以再定义其他形参
2. 在可变参数的前方可以定义其他
访问权限修饰符 :
public : 公共的 -> 在同一个模块下都可以访问
protected : 受保护的 -> 不同包有继承关系可以访问
不写 : 默认的 -> 同包下
private : 私有的 -> 同一个类中
访问权限修饰符 的访问权限大小关系 :
public > protected > 不写 > private
同一个类 | 同包不同类 | 不同包有继承关系 | 不同包无关类 | |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
不写 | √ | √ | × | × |
private | √ | × | × | × |
class : 类
interface : 接口,需定义抽象方法无方法体,实现接口(implements)重写方法
extends : 继承,继承父类
implements : 实现
abstract : 抽象的,没有方法体,子类重写方法
this : 当前对象的引用
super : 父类的
final : 最终的,变量不能修改,成员方法不能重写但能重载
static : 静态的
default : 接口中的默认方法的修饰符
instanceof : 判断类型 (大 instanceof 小)
static 不可以和 abstract,this,super
static 可以和 final , private 一起用
代码块 :
{
}
局部代码块 : 在方法内定义的代码块 -> 作用 : 控制变量的生命周期 [现在不用了]
构造代码块 : 在类中方法外的代码块
{
}
执行时机 : 每创建一个对象执行一次构造代码块 , 构造代码块是优先于构造方法执行的
构造代码块的作用 : 为对象的创建做准备工作
举例 : 对象 -> 皇帝 构造代码块 -> 宫女太监
静态代码块 : 在类中方法外被static修饰的代码块,对类变量进行初始化
static {
}
执行时机 : 当类第一次被使用时[类第一次被加载],静态代码块执行; 且执行一次! -> 优先于 构造代码块和构造方法
静态代码块的作用 : 为类的加载做准备工作
举例 : 静态代码块 -> 驱动 [类的驱动]
内部类 : 在类的内部定义的类叫 内部类
内部类的分类 :
1. 成员内部类 [理解]
2. 静态成员内部类 [理解]
3. 局部内部类 [了解]
4. 匿名内部类 [重要]
成员内部类 [理解] : 在类中方法外定义的类叫成员内部类
格式 :
public class Outer{//外部类
//成员内部类
权限修饰符 class Inner{//内部类
}
}
成员内部类的访问问题 :
1. 在内部类中访问外部类成员 :
思路是 : 直接访问, 如果访问不了,在成员的前面加 Outer.this.
案例:
//外部类特有的
System.out.println("numOut = " + /*Outer.this.*/numOut);
/*Outer.this.*/showOut();
//内外部类都有的
int num = 666;
System.out.println("num = " + num);//局部位置的num
System.out.println("this.num = " + this.num);//本类成员位置上的num
System.out.println("Outer.this.num = " + Outer.this.num);//外部类成员位置的num
this.show();//本类[Inner]的show方法
Outer.this.show();
2. 在外部类中访问内部类成员 : 创建内部类对象即可,使用内部类对象调用成员
3. 在第三方类中访问内部类成员 : 创建内部类对象并使用内部类对象访问内部类成员
创建内部类对象
Outer.Inner inner = new Outer().new Inner();
成员内部类何时使用 : 当一类事物只为另一类事物服务时,那么就可以把这一类事物定义在外部类的内部
举例 : 人 -> 心脏 / 汽车 -> 发动机 / 集合 -> 迭代器
静态成员内部类[理解] : 被 static 修饰的成员内部类 叫静态成员内部类
格式 :
public class Outer{//外部类
//静态成员内部类
权限修饰符 static class Inner{//内部类
}
}
静态成员内部类的访问问题 :
1. 在静态成员内部类中访问外部类成员 : 创建外部类对象
2. 在外部类中访问静态成员内部类的成员 : 创建静态成员内部类对象
3. 在第三方类中访问静态成员内部类的成员 : 创建静态成员内部类对象
Outer.Inner inner = new Outer.Inner();
静态成员内部类比成员内部类要使用更频繁! //定义成员内部类首选静态成员内部类
局部内部类 [了解] : 定义在方法内的类是局部内部类!
格式 :
class Outer{//外部类
权限修饰符 状态修饰符 返回值类型 方法名(形参列表){
class Inner{//局部内部类
}
}
}
局部内部类的成员访问问题 :
1. 在局部内部类中访问外部类成员 :
a . 创建外部类对象 [局部内部类在非静态方法或静态方法内都可以使用]
b . Outer.this.外部类成员 [局部内部类必须在一个非静态方法内]
//局部内部类只能在其定义的方法内使用
2. 在外部类中访问局部内部类成员 : 不可以
3. 在第三方类中访问局部内部类成员 : 不可以
匿名内部类[重要] : 是一个局部内部类
匿名 : 隐去类的名称 [匿名内部类 : 隐藏类名的一个局部内部类]
匿名内部类的使用场景 : 对接口[比较多]/抽象类/普通父类[比较少] 的另一种使用方式
格式 : 以接口为例
//创建了一个接口不知类名的实现类对象
接口名 对象名 = new 接口名(){
//重写此接口中所有的抽象方法
};
//继续拿对象名调用重写后的抽象方法