一、类与对象
1.1 对象在内存里面的存在形式:
在执行 Cat cat = new Cat(); 的时候会先在方法区里面加载Cat类的信息(属性和方法)
在进行对象实例化的时候,在堆里面开辟地址,有几个属性开辟几个内存空间地址,如果是常量(如:int)将会直接存入空间,如果是String类型将会在方法区里面的常量池里面进行存储
右边是真正的对象,左边cat只是指向对象。cat也可以叫对象名\对象引用
(该图截选自b站韩顺平Java课程)
多种名称叫法:
- 创建一个对象 = 实例化一个对象 = 把类实例化
- 成员变量 = 属性 = field(字段)
- 对象名 = 对象引用
1.2 创建对象的两种形式(与数组类似)
- 直接创建 Cat cat = new Cat();
- 先声明再创建 Cat cat; cat = new Cat();
1.3 Java内存结构分析
栈:一般存放基本数据类型
堆:存放对象(类对象、数组等)
方法区:常量池(如字符串常量),类加载信息
1.4 方法调用内存机制
(该图截选自b站韩顺平Java课程)
1.5 传参机制
对于基本数据类型来说,传递的是值,不影响main里面的实参,形参的改变不影响实参。
对于引用类型(对象、数组)来说,传递的是地址,会改变main里面的实参,可以通过形参改变实参。
1.6 构造器
- 构造器的修饰符可以默认,也可以是public protected private
- 构造器没有返回值
- 方法名和类名字必须一样
- 参数列表和成员方法一样的规则
- 在创建对象时,系统会自动调用该类的构造器完成对象的实例化
- 自己定义构造器,默认构造器会被覆盖,就不能再使用默认无参构造器,除非显示定义一下
构造器在内存里面发生的变化(重点)
1.先在方法区里面加载Person类的信息(Person.class),只加载一次
2.然后在堆中分配空间(地址)
3.对象初始化(有三步)(1.默认初始化 age=0、name=null)(2.显示初始化 age=18)(3.构造器初始化 age=22、name=小明)
4.将对象在堆中的地址返回给p(p是对象的引用,或者p叫对象名)
class Person{
int age = 18;
String name;
Person(String n,int a){
name = n;
age = a;
}
}
Person p = new Person("小明",22);
1.7 访问修饰符
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
公开 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | 没有修饰符 | √ | √ | × | × |
私有 | private | √ | × | × | × |
注意:只有默认的和public才能修饰类
二、this、super、final、static关键字
2.1 this关键字
JVM(Java虚拟机)会给每个对象分配this,代表当前对象,简单来说,哪个对象调用,this就代表哪个对象。
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(参数列表);
- 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器,必须放在第一条语句)
- this不能在类定义的外部使用,只能在类定义的方法中使用。
this.和this()不一样,this.属性是访问属性,this()是调构造器
2.2 super关键字
super代表父类的引用,用于访问父类的属性、方法、构造器
1.访问父类的属性,但不能访问父类的private属性。super.属性名
2.访问父类的方法,不能访问父类的private方法。 super.方法名(参数列表)
3.访问父类的构造器,只能放在构造器的第一句,只能出现一句。 super(参数列表);
当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问结果一样。
执行方法时,调用顺序是:(1)先找本类,有就调用。(2)找父类,有并且可以调用,则调用。(3)父类没有就找父类的父类,直到Object类。
注意:
- super()不看子类(本类),直接找父类(调用属性与类的过程一样)
- 如果查找方法的过程中,找到了,但是不能访问,则报错,cannot access
- 如果查找方法的过程中,没有找到则提示方法不存在
2.3 this和super区别
区别 | this | super |
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 从父类开始查找属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找. | 从父类开始查找方法 |
调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器首行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
2.4 final关键字
final可以修饰类、属性、方法、局部变量。
1)当不希望类被继承时,可以用final修饰。
2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
3)当不希望类的的某个属性的值被修改,可以用final修饰。
4)当不希望某个局部变量被修改,可以使用final修饰。
细节:
1) final修饰的属性又叫常量,一般用XX XX_XX来命名
2) final修饰的属性在定义时,必须赋初值,并且以后不能再修改。①定义时:如public final double TAX_RATE=0.08;②在构造器中。③在代码块中。
3)如果final修饰的属性是静态的,则初始化的位置只能是在,①定义时②在静态代码块,不能在构造器中赋值。
yaise.ELaisinet
4) final类不能继承,但是可以实例化对象。
5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
2.5 static关键字
static可以修饰类、属性、方法、代码块
- static变量是同一个类所有对象共享
- static类变量,在类加载时候就生成了,所以没有创建对象实例也可访问(对象名.类变量名)
- 可以修饰代码块,在JVM加载类时,就会执行代码块
- static方法不能被重写
2.6 final和static区别
区别 | final | static |
修饰类、方法、成员变量 | √ | √ |
构造器 | × | × |
代码块 | √ | × |
局部变量 | × | √ |
能否重写 | × | × |
注意:final和static,它们两个可以同时使用
三、“==”与equals()
3.1 “==”
==是一个比较运算符
- 既可以判断基本类型,又可以判断引用类型
- 如果判断基本类型,判断的是值是否相等。示例:int i=10; double d=10.0;
- 如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
3.2 equals
equals是Object类中的方法
- 只能判断引用类型
- 默认判断地址是否相等,子类中一般重写该方法,用于判断内容是否相等。
四、封装、继承、多态
4.1 封装
封装:把抽象出的数据属性和对数据的操作方法封装在一起,数据被保护在内部,程序需要通过被授权的操作方法才能对数据进行操作。
好处:(1)隐藏实现细节:方法(连接数据库)<--调用(传入参数..)
(2)可以对数据进行验证,保证安全合理
实现步骤:
- 将属性私有化private(不能直接修改属性)。private int name;
- 提供公有set方法(用于判断并赋值属性)。 public void setName(类型 参数名){ 业务逻辑 }
- 提供公有get方法(用于获取属性值)。 public void getName(){ return xx;}
4.2 继承
继承:解决代码复用,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
基本语法: class 子类 extends 父类{ }
细节:
- 子类继承了所有的属性和方法,但私有属性不能在子类直接访问,要通过公共的方法去访问
- 子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
- 指定调用父类的某个构造器,需要显式调用: super(参数列表)
- super在使用时,必须放在构造器第一行(super只能在构造器中使用)
- super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
- java所有类都是Object类的子类, Object是所有类的父类
- 父类构造器的调用不限于直接父类,将一直往上追溯直到Object类
- Java时单继承机制,子类最多只能继承一个父类(直接继承)
4.4 重载和重写
名称 | 范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
重载 | 本类 | 必须一样 | 类型、个数、顺序至少一个不一样 | 无要求 | 无要求 |
重写 | 父子类 | 必须一样 | 相同 | 与父类返回类型一致,或者是其子类 | 不能缩小父类方法的访问范围 |
重写的返回类型:子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类。比如父类返回类型是 Object,子类方法返回类型是String。
属性没有重写之说,属性的值看编译类型。
4.5 多态
多态:方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
体现:
- 方法的多态:重载和重写就体现多态
- 对象的多态:①一个对象的编译类型和运行类型可以不一致。②编译类型在定义对象时,就确定了,不能改变。③运行类型是可以变化的。④编译类型看定义时=号的左边,运行类型看=号的右边
Animal animal = new Dog(); //animal编译类型就是 Animal ,运行类型 Dog
//因为运行时,执行到改行时,animal运行类型是Dog,所以cry就是Dog的cryanimal.cry(); //小狗汪汪叫
/ /animal编译类型 Animal,运行类型就是 Catanimal = new Cat();
animal.cry(); //小猫瞄瞄叫
instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
String str = "hi";
System.out.printin(str instanceof Object); //true
4.5.1 向上转型
- 前提:两个对象(类)存在继承关系
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员;最终运行效果看子类的具体实现。
4.5.2 向下转型
- 语法:子类类型 引用名 = (子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 可以调用子类类型中所有的成员