18 面向对象
18.1基本概念
18.1.1 对象
万物皆对象,对象有特征和行为,也就是属性和方法。
例如
人有特征 身高体重姓名。。
人有行为 奔跑 吃饭睡觉。。
18.1.2属性和方法
类是由属性和方法组成的
属性:一组对象共同拥有的特征。
方法:一组对象共同拥有的行为。
类是对象的抽象
对象是类的具体
18.1.3 实例变量
实例变量也叫成员变量,也叫属性
描述 实例变量 局部变量
定义位置 直接写在类中 方法中
默认值 默认值与数组相同 没有默认值
作用范围 整个类中 离当前变量最近的大括号以内
命名冲突 可以与局部变量重名,就近原则,先用局部变量,需要用实例变量要加 this.前缀 作用范围内,不能重名
存储位置 全部存储在堆中,所有才有默认值 基本数据类型存储在栈中;引用数据类型,名字在栈,值在堆中。
声明周期 随着创建对象而生效,随着对象被垃圾回收而死亡 随着方法的入栈而生效,随着方法的出栈而死亡。
18.1.4 方法重载
1:同一个类中
2:方法名相同
3:参数列表不同(参数的 个数 类型 顺序 有一个不同则为不同)。
<访问修饰符,返回值是否不同不作为方法重载的参考条件>
调用带有重载的方法时,需要根据传入的实参去找到与之匹配的方法。
好处:屏蔽使用差异,灵活,方便
18.1.5 构造方法
用于创建对象的特殊方法
无返回值
无参构造:jvm默认提供的,如果书写了有参构造而没有手动书写无参构造,无参构造会被覆盖
有参构造方法的重载:
和方法重载相同,只有一条要求,参数列表不同。
//语法:
//访问修饰符(public)+ 类名(形参列表){}
public class Student{
String name;
int age;
public Student(){
}
public Student(String name,int age){
this.name=name;
this.age=age;
}
}
18.1.6 this关键字
表示当前对象。
适用场景
1:可以访问属性,在和形参重名或者和局部变量重名时可以用this.实例对象
2:可以访问方法
3:可以访问构造方法
在构造方法重载时可以代替重复的部分
this()放在第一行
public Person(String name) {
this.name = name;
}
public Person(String name,int age) {
this(name);
this.age = age;
}
public Person(String name,int age,String sex) {
this(name,age); // 调用本类构造 必须在构造方法的第一句
this.sex = sex;
}
18.2 封装
解决用户不合理的给属性赋值的情况,可以用封装来实现
步骤:
1:属性私有 使用private修饰符,表示此属性只能在本类中访问而其他类中无法访问
2:方法公开,
set:每一个属性值编写set 方法输入值,命名为setName(),遵守驼峰原则可对输入值进行操作,如判断。
//setName()需要参数,但不需要返回值
public void setName(String name){
this.name=name;
}
get:编写get方法获取值,命名getName()
//getName()不需要参数,但是需要返回值
public int getName(){
return name;
}
简言之:一个属性设置了private修饰符,通过set输入,通过get输出活的,限制了用户的操作
封装的优点:
1:便于使用者正确使用系统,防止错误修改属性
2:降低了构建大型系统的风险
3:提高程序的可重用性
4:降低程序之间的耦合度
耦合度:
灵活性,可更改自定义增删改的地方越多,耦合度越小,越小越好。
例如乐高(低耦合度)和塑料奥特曼(高耦合度)
18.3 继承
18.3.1概念
继承是java中实现代码重用的重要手段。java中只支持单根继承,即一个类中只能有一个直接父类
子类和父类必须符合is-a关系
使用extends关键字实现继承
18.3.2 super关键字
super关键字:表示父类对象
只能用在子类的方法和构造方法中
可以访问父类访问权限允许的:
属性:super.父类属性名 属于可访问权限的属性 protected 和public 修饰的
方法:super.方法() 属于可访问权限的方法 protected 和 public 修饰的
构造方法:super()调用父类无参构造方法
super(形参列表)调用父类有参构造方法
1:访问父类构造方法必须在子类构造的第一句
2:子类构造默认访问父类的无参构造方法,除非显式调用父类的有参构造方法。
也就是说,子类必须访问父类的有参或者无参构造其中一个
19 修饰符
19.1 基本修饰符
调用时提示图案
private 红色方块
默认修饰符 蓝色星星
protected 黄色菱形
public 棕色圆形
19.2 类的访问修饰符
public :公开的访问权限,本项目的任何位置都可以访问此类
默认不写:表示只能在同一个包中访问此类
19.3 类成员的访问修饰符
private:本类
默认不写:本类 ,本包
protected:本类,本包,子包
public :任何位置
20 Static 关键字
20.1修饰属性
可以用来修饰成员变量
静态变量可以直接通过类名访问
static关键字修饰的属性,在内存中只有一份,可以用于数据的共享,共享节省空间
推荐使用 类名.属性名访问,因为该变量属于整个类,不属于任何对象
是被所有当前类对象共享的数据
类的成员变量包括
类变量(静态变量)
被static修饰的变量
在内存中只有一个拷贝
类内部,可在任何方法内直接访问静态变量
其他类中,可以直接通过类名访问
实例变量
没有被static修饰的变量
每创建一个实例,就会为实例变量分配一次内存,实例变量在内存中有多个拷贝,互不影响
20.2 修饰方法
static修饰的方法可以通过类名访问
没有static修饰的方法只能通过new对象访问
静态方法:可直接通过类名访问
静态方法中不能使用this和super访问所属类的实例变量和实例方法
可直接访问类的静态变量和静态方法
实例方法:通过实例访问
可直接访问所属类的静态变量、静态方法、实例变量和实例方法
静态方法必须被实现
20.3修饰代码块
static{
}
静态代码块
当java虚拟机加载类时(只加载一次),就会执行该代码块,有多个代码块时按顺序加载
类加载的时机:
1:new对象时会加载类
2:访问类中的信息会加载类,比如访问静态变量
适用场景:
用于实现一些前置的,必须要执行的操作,并且只执行一次。比如数据初始化
20.4 static关键字实现原理
方法区:方法区是SUN公司提出的一个规范,JDK8之前称为永久代,JDK8开始称为元数据。
静态区:是指static相关的信息存储区域。
创建对象的过程:
1:当我们new对象时,JVM会先在方法区中检查,是否此类的class文件已经被加载。
如果没有被加载,则会先将class文件加载到方法区,此时静态方法的相关信息会初始化。
2:在堆中开辟空间,此时实例属性将有默认值
3:将栈中的引用指向堆中的空间
永久代/元数据:因为在方法区中初始化的数据不会随着程序的执行动态被垃圾回收,所以存储的时间比较长。另外因为方法区中的数据初始化的时机比较早,所以后来被称为元数据
20.5 关于访问
平级和平级的互相直接访问
非静态可以直接访问静态
静态不能直接访问非静态,必须先new对象
21 全限定名
当调用的两个类 同名 且来 自不同包 时
一个可以正常定义,另一个要用全限定名
com.qfedu.test6.B b1 = new com.qfedu.test6.B(); // 全限定名 指包名 + 类名
22方法重写
22.1 特点
方法重写Override的特点:
1:存在于父子类之间 父类–子类
2:方法名与父类相同 同名
3:参数列表与父类相同 形参相同
4:返回值与父类相同(或者其子类) 返回值相同
5:访问权限不能严于父类(不能窄化父类的访问权限) 大于等于父亲
根据 private 默认 protected public 从小到大顺序
6:不能抛出比父类更多的异常
7:父类静态方法可以被继承,不能被重写
22.2 @Override注解
此注解用于表示子类的方法属于重写父类的方法
写上注解后一定要按照重写规则写,否则会报错
只能用在子类的方法上
此注解不影响代码的执行结果,只用于规范方法重写
次注解可以提高代码的阅读性
22.3重写和重载的区别
重写:重写父类方法,比如object和String系统类
重载:同一个类中同名不同形参函数
23 Object类
所有类的默认父类都是Object
23.1 重写toString()类
Object和String中的toString()类
Object类中的toString方法默认返回值为:包名 + 类名 + @ + hash值
我们可以根据自己的需求进行重写,比如我们可以重写为返回值为:属性+属性值
直接打印对象名相当于访问此对象的toString方法
public String toString() {
return "Dog[name:"+super.name+" health:"+health+" love:"+love+" strain:"+strain+"]";
}
23.2 面试题 重写equals()
==和equals()的区别?
==比较基本数据类型比较的是值
==比较引用数据类型比较的是地址
equals()本身也是比较地址,但是我们可以重写按照我们的要求来比较
String 类就的equals就是重写了Object中的equals()
equals()重写
Object和String
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
package oop_Override_Test;
/**
* 需求:假设现在有"两个人",名字和身份证都一样,那么这"两个人"就是同一个人,,所以使用equals比较应当为true
*
* @author Administrator
*
*/
public class Test1 {
private String name;
private String idCard;
public Test1() {
}
public Test1(String name, String idCard) {
super();
this.name = name;
this.idCard = idCard;
}
//封装
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public boolean equals(Object obj) {
if(this==obj) {
return true;
}
Test1 test1=(Test1)obj;
if(this.getName().equals(test1.getName())&& this.getIdCard().equals(test1.getIdCard())) {
return true;
}
return false;
}
public static void main(String[] args) {
Test1 t1=new Test1("王贺","411527200001303515");
Test1 t2=new Test1("王贺","411527200001303515");
System.out.println(t1.equals(t2));
}
}
总结
重写object的toString()
重写equals()
23.3 重写hashCode()
hashCode属于一种杂凑算法:是指将一些杂乱的条件拼凑在一起计算出来一个值
java中的hash值是根据地址加上其他的一些条件计算出来的一个十进制的数值
为什么要重写hashCode()?
因为在一些散列数据结构中,是根据两个对象的equals比较为true,并且hashCode相同则认为是重复的对象,的这种规则来存储元素的。所以在实际开发中,通常要重写equals,也会重写hashCode。
当两个变量完全相同时,equals比较相同,但是hash值不同,不符合要求,所以要重写。
hash值是根据地址计算的,不代表地址,地址是不能变的,除非对象赋值
如:People p1=new People();
People p2=p1;//此时p1和p2的地址指向相同
public int hashCode() {
int prime = 31; // 权重
int result = 1;
result = prime * result + (this.getName() == null ? 0 : this.getName().hashCode());
result = prime * result + (this.getIdCard() == null ? 0 : this.getIdCard().hashCode());
return result;
}
为什么权重选择31?
31是个很特殊的质数(素数)
任何数乘以31等于这个数左移5位,减去这个数本身
左移几位表示乘以2的几次方
右移几位表示除以2的几次方
左移是先转化为二进制,再左移,省去了转为二进制后再左移的步骤,效率较高
23.4 getClass
getClass()方法可以获取当前对象的信息 包名+类名
24 类型的属性值
当所写的属性需要一种规范,而当前的数据类型无法满足时,就要自定义数据类型对2数据进行存储。
例如,要存一个人的地址,包含省,市,区
就要自定义类Address 封装,重写toString
public class Address {
private String province;
private String city;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city + "]";
}
}
在调用自己的类中也把自己封装,重写toString
public class People {
private String name;
private Address address;
private Hobby[] hobbies;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Hobby[] getHobbies() {
return hobbies;
}
public void setHobbies(Hobby[] hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "People [name=" + name + ", address=" + address + ", hobbies=" + Arrays.toString(hobbies) + "]";
}
}
在主函数中创建对象,赋值,并赋给People
People p1=new People();
p1.setName("名字");
Address address=new Address();
address.setProvince("省份");
address.setCity("市");
p1.setAddress(address);
Hobby[] hobbies=new Hobby[3];
hobbies[0]=new Hobby();
hobbies[1]=new Hobby();
hobbies[2]=new Hobby();
hobbies[0].setHobbyType("体育");
hobbies[1].setHobbyType("电影");
hobbies[2].setHobbyType("动漫");
hobbies[0].setHobbyName("a");
hobbies[1].setHobbyName("b");
hobbies[2].setHobbyName("c");
p1.setHobbies(hobbies);
System.out.println(p1);