面向对象
参考尚硅谷宋红康老师的视频 底部有链接
1.类与对象
类:相当于图纸
属性:这一类事物拥有的共同属性
动作:这一类事务共同执行的功能
对象:使用类创建的具体的某一个东西
代码里面:
类要用class来定义
1. 属性:
不同点:
1.声明的位置不同
1.1. 写在方法里或者方法的形参的变量,叫做局部变量,是方法的属性
1.2. 写在类里面的变量,叫做成员变量,就是类的属性
-
关于权限修饰符的不同
2.1 属性:可以在声明属性时,指明其权限修饰符
常见的权限修饰符 public private 缺省 protect
2.2 局部变量:不可以使用权限修饰符。
2.3 默认初始化值得情况
属性: 类的属性,根据其类型,都有默认的初始化值。
整型(byte、short、int、long):0;
浮点型(float、double)0.0
字符型(char):0
布尔型(boolean):false
引用数据类型(类、数组、接口):null
局部变量:没有默认的初始化值
特别的:形参在调用时,赋值即可。
2.4 在内存中加载的位置
属性:加载到堆空间中
局部变量:加载到栈空间中
相同点:
1. 定义变量的格式:数据类型 变量名=变量值;
2. 先声明,后使用;
3. 变量都有其对应的作用域
4.
2. 方法:
- 方法的声明:
权限修饰符 返回值类型 方法名(形参列表){
方法体;
}
-
说明:
2.1 权限修饰符:常见的权限修饰符 public private 缺省 protect
2.2 返回值类型:有返回值and没有返回值
如果方法有返回值,则必须在方法声明时指定返回值类型,同时方法中要使用return来 指定类型的变量或常量。
如果方法没有返回值吗,则方法声明时,用void来表示。通常,没有返回值中就不用 return。但是,如果使用的话,只能“return;”,表示结束此方法
成员方法:在类里面的方法(不写static的方法)
创建对象:类 引用 = new 类();
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static)
意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性的值。
public class Car {
//成员变量
String color;//车的颜色
int speed;//车的颜色
int seat;//车的座位
//成员方法
public void run(){
System.out.println("车能跑");
}
public static void main(String[] args) {
//int a=10;//局部变量
//创建对象
Car car=new Car();
//让车跑
//对象或者引用.方法()
car.run();//,表示调用 “的”
//car.pailiang=1.5; //类中没有定义的内容不可以使用
car.color="绿色";
car.speed=100;
car.seat=7;
}
}
2.this关键字
this:表示当前类的对象
this可以在方法内部获取到对象中的属性信息
this还可以区分局部变量和成员变量
public class Car {
String color;
int speed;
int seat=5;
public void run(){
//默认会有一个this:当前正在执行方法的对象
//获取到车的颜色和座位
//System.out.println(this);
System.out.println(this.color);
System.out.println(this.speed);
System.out.println("车能跑");
}
public void fly(String color){
System.out.println(this.color+"的颜色的车会飞,飞在"+color+"颜色的云彩里");//如果不加this,访问的也是成员变量
//变量的查找顺序:先找自己的方法内,如果自己没有,就去this里面找。
}
public static void main(String[] args) {
/* Car c=new Car();
c.color="红色";
c.speed=150;
c.run();//在调用方法的时候,Java会自动的把对象传递给方法,在方法中由this来接收对象
//System.out.println(c);
Car c2=new Car();
c2.color="绿色";
c2.speed=180;
c2.run();
*/
Car c=new Car();
c.color="绿色";
c.fly("黑色");
}
}
3.构造方法
在创建对象的时候,自动调用的方法,
语法:
public 类名(参数){
}
注意:
1. 没有返回值这一项
-
在我们new对象的时候,自动调用构造方法
作用:在创建对象的时候,给对象设置属性信息,
java会默认自动送给每一个类一个无参的构造方法,但是,如果自己写了构造方法,系统就不再赠送了。
4.方法的重载
1. 重载
-
定义:在同一个类中允许存在一个以上的同名方法,只需要他们的参数个数或者参数类型不同即可
-
“两同一不同”:同一个类、相同的方法
参数列表不同:参数个数不同,参数列表不同
-
-
判断是否重载:
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系
-
在通过对象调用方法时, 如何确定某一个指定的方法
- 方法名 ===》参数列表
2. 构造方法的重载
构造方法也是方法,也可以进行重载
作用:可以有更多的方式去创建对象
使用this可以访问其他的构造方法。
this()
public class DaXia {
String name;
String waihao;
int age;
String bangPai;
public DaXia(String name,int age,String bangPai){
this.name=name;
this.age=age;
this.bangPai=bangPai;
}
//构造方法也是方法,也可以进行重载
//可以让我有更多的方式去创建对象
public DaXia(String name,int age,String bangPai,String waihao){
this(name,age,bangPai);//this还可以调用当前类中的其他的构造方法
this.waihao=waihao;
}
public static void main(String[] args) {
DaXia daXia=new DaXia("岳不群",18,"华山派");
DaXia daXia1=new DaXia("武松",19,"梁山","行者");
}
}
5.方法的封装
-
面向对象的特征一:封装与隐藏
一. 问题的引入:
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制 条件的添加。(比如:setLegs())同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明 为私有的(private).
==》此时,针对于属性就体现了封装性。
二. 封装性的体现:
我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置 (setXxx)此属性的值
三. 封装性的体现,需要权限修饰符来配合。
-
Java规定的4种权限(从小到大排列):private、缺省、protected 、public
-
4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
-
具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
总结:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
import java.util.Date;
public class Test {
private String name;
private String sex;
private Date birthday;
private double phone;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public double getPhone() {
return phone;
}
public void setPhone(double phone) {
this.phone = phone;
}
}
6.面向对象练习
-
用面向对象的思维来模拟LOL里的盖伦打架;
public class Hero { String name; String skill_q; String skill_w; String skill_e; String skill_r; public Hero(String name,String skill_q,String skill_w,String skill_e,String skill_r){ this.name=name; this.skill_q=skill_q; this.skill_w=skill_w; this.skill_e=skill_e; this.skill_r=skill_r; } public void fight(){ System.out.println(this.name+"在打架"); } public static void main(String[] args) { Hero h1=new Hero("盖伦","小宝剑","防御","电风扇","大宝剑"); h1.fight(); } }
-
用面向对象的思维来完成植物大战僵尸
植物.java
-
public class ZhiWu {
String name;
int hp;
int attack;
public ZhiWu(String name,int hp,int attack){
this.name=name;
this.hp=hp;
this.attack=attack;
}
public void fight(JiangShi js){
System.out.println(this.name+"正在打"+js.name);
js.hp -=this.attack;
System.out.println("僵尸的血量剩余"+js.hp);
}
}
僵尸.java
public class JiangShi {
String name;
int hp;
int attack;
public JiangShi(String name,int hp,int attack) {
this.name = name;
this.hp = hp;
this.attack = attack;
}
public void eat(ZhiWu zw){
System.out.println(this.name+"正在吃植物"+zw.name);
zw.hp -=this.attack;
System.out.println("植物的血量还有"+zw.hp);
}
}
场景.java
public class Client {
//创建植物和僵尸
public static void main(String[] args) {
ZhiWu zhiWu=new ZhiWu("豌豆",1000,5);
JiangShi jiangShi = new JiangShi("铁通僵尸",800,20);
zhiWu.fight(jiangShi);
jiangShi.eat(zhiWu);
}
}
7.static关键字
static静态
有多少人就要改多少次
静态的内容在内存中是保留一份的,并且各个对象之间进行共享
使用p1.country=”民国“,不推荐这样使用静态变量
推荐使用类名去访问静态的内容
public class Person {
String name;
static String country="大清";//他是共享的
String address;
public Person(String name,String address){
this.name=name;
this.country=country;
this.address=address;
}
public static void main(String[] args) {
Person p1=new Person("张三","八大胡同");
Person p2=new Person("李四","朝阳门");
//大清亡了
Person.country="民国";
System.out.println(p1.country);
System.out.println(p2.country);
}
}
特点:
- 数据共享
- 属于类的 并不属于对象
- 优先于对象产生的
通用代码块,静态代码块
创建对象的过程:
1. 静态构造器
2. 通用构造器
3. 构造方法==》创建对象
public class Test {
{
System.out.println("通用代码块");
}
static{
System.out.println("静态代码块");
}
public Test(){
System.out.println("构造方法");
}
public static void main(String[] args) {
new Test();
}
}
由于创建对象的过程是在静态内容加载完成之后,在静态方法和静态块里不能使用this
可以在静态方法里直接调用静态的方法
public static void pen(){
System.out.println("aaa");
}
public static void main(String[] args) {
pen();
}
静态内容使用类名去访问
8.方法的继承
继承:子类可以自动拥有父类中除了私有内容外的其他所有内容。子类是对父类的功能进行扩展
当出现x是一种y的时候,x就可以继承y
黑熊怪是一种妖怪
public class HeiXiong extens YaoGuai(){
}
-
好处:
1.1 减少了代码的冗余,提高了代码的复用性
1.2 便于功能的扩展
1.3 为之后多态性的使用,提供了
-
格式:
class A extends B{}
2.1 体现:一旦子类A继承父类B以后,子类中就获取了父类B中的声明的所有的属性和方法
特别的,父类中声明为private的属性和方法,子类继承父类后,仍然认为获取了父类中的私有的结构,只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
2.2 子类继承父类以后,还会声明自己特有的属性或方法;实现功能的拓展。
子类和父类的关系,不同于子集和集合的关系
-
Java关于继承性的规定
-
一个子类只能有一个父类
-
Java单继承:一个父类可以派生出多个父类
-
子父类是相对的概念
-
子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
-
子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法。
-
9.super关键字
super:表示父类中的内容
this:表示自己类中的内容
用super和this来区分父类和子类中重名的内容
super关键字的使用
-
super理解为:父类的
-
super可以用来调用:属性、方法、构造器
-
super的使用:调用属性和方法
-
我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
-
特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
-
特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
-
-
super调用构造器
-
我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
-
"super(形参列表)"的使用,必须声明在子类构造器的首行!
-
我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
-
在构造器的首行,没有显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
-
在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器创建对象的过程:先创建父类的对象,然后创建子类的对象在子类构造方法的第一行,会默认调用父类的构造方法
-
总结:
-
super可以获取到父类中的内容
-
可以调用父类中的构造方法,必须写在子类构造方法的第一行,如果父类的构造方法是无参的,可以不写,如果父类没有无参的构造方法,必须要写super
10.方法的重写
1. 重写:
子类继承父类后,可以对父类中的同名参数的方法进行覆盖操作
语法:子类和父类中的方法的声明完全一致
重写又被称为方法的覆盖
-
应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名参数的方法时,实际执行的是子类重写父类的方法。
-
面试题:区分方法的重载和重写
把他们的作用和定义说清楚和细节点说清楚
-
规定:
-
方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws异常的类型{
//方法体
}
约定:子类中的叫重写的方法,父类中的叫被重写的方法
-
子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
-
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明private权限的方法
-
返回值类型:
父类中被重写的返回值的类型是void,则子类重写的方法的返回类型只能是void
父类被重写的方法的返回值类型是A类型,则子类重写的返回值类型可以是A类或者A类的子类
父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
-
子类和父类的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)
Person.java
public class Person { String name; int age; public Person(){ } public Person(String name,int age){ this.name=name; this.age=age; } public void eat(){ System.out.println("吃饭"); } public void walk(int distance) { System.out.println("走了"+distance+"公里"); } }
Student.java
public class Student extends Person{ String major; public Student(String major) { this.major = major; } public Student() { } public void study(){ System.out.println("学习,专业是"+major); } //对父类中的eat方法进行了重写 public void eat() { System.out.println("学生应该多吃有营养的东西"); } }
PersonTest.java
public class PersonTest { public static void main(String[] args){ Student s =new Student("计算机科学与技术"); s.eat(); s.walk(10); s.study(); } }
-
2. 面试题:重载和重写
说清他们的细节点和定义
说清重写的权限修饰符、返回值类型是什么等等
11.多态
-
理解多态性:可以理解为一个事物的多种形态。
-
何为多态性:
对象的多态性:父类的引用指向子类对象(或者子类的对象赋给父类的引用)
-
多态的使用:虚拟方法的调用
有了以下这种多态的格式后,在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
Father f=new Son();
总结: 编译看左,执行看右
-
多态性的使用前提:
- 类的继承关系
- 要有方法的重写
-
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
12. 向下转型
-
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类生命的属性和方法。子类特有的方法不能调用。
-
如何才能调用子类特有的属性和方法呢?
向下转型:使用强制类型转换符
-
使用强转时,可能会出现ClassCastException的异常
14.instanceof关键字
-
instanceof的使用: a instanceof A:判断对象a是否是A的实例。如果是,返回true;如果不是,返回false
-
使用情景:为了避免在乡下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
-
==:对于引用类型来讲,比较的是两个引用数据类型变量的地址值是否相同
15.多态练习
/*
* 练习:
* 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
* 系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
*
* 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
* 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
*/
clas
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 FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;//多态性
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b == s);//true
System.out.println(b.count);//10
b.display();//20
}
}
16. static关键字
-
static:静态的
-
static可以用来修饰:属性、方法、代码块、内部类
-
使用static来修饰属性:
-
属性按是否使用static修饰,又分为:静态属性和非静态属性(实例变量)
1. 实例变量:我们创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。 2. 静态变量:我们创建了多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
-
static修饰属性的其他说明:
1. 静态变量随着类的加载而加载。可以通过“类.静态变量”的方式进行调用 2. 静态变量的加载早于对象的创建。 3. 由于类只会加载一次,则静态变量在内存中也会存在一份
-
-
-
使用static来修饰方法:静态方法
-
随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
-
静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
-
static注意点:
1. 在静态方法内,不能使用this关键字、super关键字。 2. 关于静态属性和静态方法的使用,可以从生命周期的角度去理解
-
在开发中,如何确定一个属性是否要声明为static?
1. 属性是可以被多个对象所共享的,不会苏浙对象的不同而不同的 2. 类中的常量也常声明为static
-
在开发中,如何确定一个方法是否要声明为static?
1. 操作静态属性的方法,通常就设置为static 2. 工具类中的方法,习惯上声明为static
-
17.代码块
-
作用:用来初始化类或对象
-
代码块只能用static来修饰
-
分类:静态代码块和非静态代码块
-
静态代码块
- 内部可以有输出语句
- 随着类的加载而执行,而且只执行一次
- 作用:初始化类的信息
- 如果一个类中定义了多个的静态代码块,则按照声明的先后顺序执行
- 静态代码块的执行要优先于非静态代码块的执行
- 只可以调用静态的属性或方法,不能调用非静态代码块的结构
-
非静态代码块
- 内部可以有输出语句
- 随着对象的创建而执行
- 而且,每创建一次对象,就执行一次非静态代码块
- 作用:可以在创建对象时,可以对对象的属性进行初始化
- 可以调用静态的属性、静态的方法、非静态的属性、非静态的方法
-
对属性可以赋值的位置:
- 默认初始化
- 显示初始化 / 在代码块中赋值-
- 构造器中初始化
- 有了对象以后,可以通过“对象.属性”或者“对象.方法”的方式,进行赋值
执行的先后顺序:1=>2=>3=>4
总结:由父及子,静态先行
18. final关键字
-
final可以用来修饰的结构:类、方法、变量
-
final用来修饰一个类:此类不能被其他类继承
-
final用来修饰方法:不可以再被重写
-
final用来修饰变量:此时的“变量”就称为是一个常量
- final修饰属性:可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化
- final修饰局部变量:
- 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
static final 用来修饰属性:称为全局常量
19. 抽象类和抽象方法
-
abstract可以用来修饰的结构:类、方法
-
abstract修饰类:抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化调用(涉及:子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
-
abstract修饰方法:抽象方法
1. 抽象方法只有方法的声明,没有方法体 2. ==包含抽象方法的类一定是个抽象类==。反之,抽象类中可以没有抽象方法 3. 若子类重写了父类中的所有的抽象方法后,此子类可以实例化 若子类没有重写父类中的所有的抽象方法,此子类也是一个抽象类,需要用abstract来修饰
-
使用的注意点:
- abstract不能用来修饰:属性、构造器等结构
- abstract不能用来修饰私有方法、静态方法、final的方法
20. 接口
1. 接口
-
接口是使用interface来定义
-
在Java中,接口和类是并列的两个结构
-
如何定义接口,定义接口中的成员
- JDK7及以前:只能定义全局常量和抽象方法
- 全局常量:public static final, 但是书写时,可以省略不写
- 抽象方法:public abstract的
- JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法。
- JDK7及以前:只能定义全局常量和抽象方法
-
接口中不能定义构造器,意味着接口不可被实例化
-
Java开发中,接口通过让类去实现(implements)的方式来使用
- 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
- 如果实现类没有覆盖接口中的所有抽象方法,则此实现类仍为一个抽象类
-
Java类可以实现多个接口—>弥补了Java的单继承的局限性
格式: class AA extends BB implements CC,DD,EE
-
接口与接口之间可以继承,而且是多继承
-
接口的使用,
- 体现了多态
- 接口实际上,可以看作是一种规范
- 开发中,体会面向接口编程
2. 面试题:抽象类与接口有哪些异同?
https://www.bilibili.com/video/BV1Kb411W75N?p=1