面向对象
初识面向对象
-
面向过程思想
步骤清晰简单,第一步做什么,第二步做什么……(形似线性)
面向过程适合处理一些较为简单的问题
-
面向对象思想
物以类聚,分类的思维模式,思考问题首先 会解决问题,需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
-
什么是面向对象
-
面向对象编程(Object-Oriented Programming,简称 OOP)。
-
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
-
面向对象是抽象的。
-
三大特性
- 封装
- 继承
- 多态
-
-
面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
-
对于描述复杂的事物,为了从宏观上把握、从整体上分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍需要面向过程的思路去处理。
方法回顾加深
方法的定义
- 修饰符
- 返回类型
- break、return
- 方法名:符合规范,见名知意
- 参数列表:(参数类型 参数名)
- 异常抛出
方法的调用
-
静态方法
-
调用静态方法可以直接使用 类名.方法名 去调用。
-
static方法不属于对象,是属于类,随着类的加载而加载。
-
在静态方法中,不可以直接调用非静态方法,要想调用,必须先实例化对象,然后再用 对象名.非静态方法 才可以。同理,在静态方法中,不可以直接调用非静态变量,可以先实例化对象,然后再用 对象名.非静态变量。
-
-
非静态方法
调用非静态方法需要先 实例化这个类(new) 然后 再通过 实例化变量.方法名 去调用。
- 静态方法和非静态方法的代码举例
- 形参和实参
-
**值传递和引用传递 **
-
this关键字
对象的创建分析
类与对象的关系
类是一种抽象的数据类型,它是对某一类事物整体的描述(这类事物应该具备的特点和行为),但是代表不了一个具体的事物。(动物类、植物类、学生类等)
对象是抽象概念的 具体实例。
创建和初始化对象
-
使用new关键字创建对象
使用new关键字创建的时候,除了 分配内存 空间以外,还会给创建好的对象 进行默认的初始化 以及对 类中构造器的调用。
-
类中的构造器
类中的构造器也被称为 构造方法 ,是在进行创建对象的时候必须要调用的。并且具有以下两个特点。
-
必须和类的名字相同
-
必须没有返回类型,也不能写void
-
代码演示
package com.Hrstudy.oop.demo02; public class Application { public static void main(String[] args) { //new 实例化了一个对象 Person person = new Person(); System.out.println(person.name); //huangran } /* * 构造器: * 1.和类名相同 * 2.没有返回值 * 作用: * 1.new 本质是在调用构造方法 * 2.初始化对象的值 * 注意点: * 1.定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造 * */ } //Person类 package com.Hrstudy.oop.demo02; public class Person { // 一个类即使什么都不写,它也会存在一个无参方法 //显示的定义构造器 String name; //实例化初始值 //1. 使用new关键字,本质是在调用构造器 public Person () { this.name = "huangran"; } //有参构造:一旦定义了有参构造,就必须显示定义无参构造 public Person(String name){ this.name = name; } //快捷键:ALT + insert 可以调出创建构造器 }
- //快捷键:ALT + insert 可以调出创建构造器
-
创建对象的内存分析
代码:
画图分析:
类与对象的简单小结
-
类是一个模板(抽象的),对象是一个实例(具体的)。
-
方法
定义、调用!
-
对象的引用
引用类型:除了八种基本类型的其余类型
对象是通过引用来操作的: 栈–>堆
-
类中的属性
也称作字段名(Filed)、成员变量
默认均有初始化:
数字: 0 0.0
char: u0000
boolean: false
引用: null
创建属性均使用 属性类型 属性名 = 属性值!
- 对象的创建和使用
-
必须使用 new 关键字创造对象,构造器 (构造方法) Person huangran = new Person( );
-
对象的属性 huangran.name
-
对象的方法 huangran.sleep( )
- 类:
静态的属性 属性
动态的行为 方法
面向对象的三大特性
封装
- 该露的露,该藏的藏
我们程序设计要追求 “高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低 耦合就是仅暴露少量的方法给外部使用。
- 封装也就是数据的隐藏
通常,应该禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这成为信息隐藏。
-
属性私有,get/set
-
代码演示
主方法:
package com.Hrstudy.oop.demo03; public class Application { public static void main(String[] args) { //new 实例化了一个对象 Student s1 = new Student(); s1.setName("美女"); System.out.println(s1.getName()); s1.setAge(1111); System.out.println(s1.getAge()); } }
Student类:
package com.Hrstudy.oop.demo03;
//类 private:私有
public class Student {
//属性私有
private String name;//名字
private int id; //学号
private char sex; //性别
private int age; //年龄
//提供一些可以操作这个属性的方法!
//get 获取这个数据
public String getName(){
return this.name;
}
//set 给这个数据设置值
public void setName(String name){
this.name = name;
}
//Alt + Insert 快捷键创建get/set方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 120 || age < 0){ //通过set方法可以在程序内部保证数据准确性
this.age = 3;
}else{
this.age = age;
}
}
/*
封装特性:
1. 提高程序的安全性,保护数据
2. 隐藏代码的实现细节
3. 统一接口
4. 系统的可维护增加了
*/
}
- //Alt + Insert 快捷键创建get/set方法图示
继承
继承和类的关系
-
继承是类和类之间的一种关系,除此之外,类和类之间关系还有依赖、组合、聚合等。
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends
-
Java类中只有单继承,没有多继承。(意思是,一个儿子只能有一个亲爸爸,但一个爸爸可以有多个儿子)
-
在Java中,所有的类,都默认直接或者间接继承Object。
代码演示
- 快捷键展现 类的族谱(Ctrl + H)
super 关键字
- 子类使用 super 操作父类的属性
- 子类使用 super 操作父类的方法
子类的无参构造方法默认使用super调用的父类的无参构造
- 私有方法的无法被继承
super注意点
-
super调用父类的构造方法,必须写在子类构造方法的第一行。
-
super只能出现在子类的方法或者子类的构造方法中。
-
super 和 this 不可以同时调用构造方法!
-
父类出现有参构造前提下,如果子类需要创造无参构造,父类中必须显示声明无参构造,否则会报错!
-
super 和 this 不同点
-
代表的对象不同
this : 本身调用者的这个对象
super :代表父类的对象的引用
-
前提条件不同
this :没有继承也可以使用
super : 只能在继承的条件下使用
-
构造方法含义不同
this() : 本类的构造
super(): 父类的构造
-
方法重写
-
方法重写需要有继承关系,子类重写父类的方法。
-
方法名必须相同,参数列表必须相同(快捷键 alt+insert --> override)
-
被重写的方法修饰符,范围可以扩大,不可以缩小。 pubic>protected>default>private
-
方法重写后抛出的异常,范围可以缩小,不可以扩大! ClassNotFoundException --> Exception(大)
-
方法重写是为了满足 父类未提供给子类的功能 。
-
代码演示
- 父类和子类都是静态方法
静态方法: 实例化,静态方法的调用只和左边定义的数据类型有关。静态的方法和非静态的放法区别很大,不要随意使用静态方法。
- 父类和子类非静态方法
父类的引用指向了子类:
如果被调用方法,在子类和父类中均存在,最终执行的被调用方法,取决于子类对象中是否存在被调方法的重写,若存在,则最终执行的是子类中的,否则,是父类中的。(子类中有调用子类)。
多态
多态相关知识点
- 多态实现了动态编译(可扩展性增强)。
- 即同一个方法可以根据发送对象的不同采用多种不同的行为方式。(根据下面的代码体验)
- 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多。(父类、有关系的)
- 多态是方法的多态,和方法重写一样,属性也不具有多态性。
- 多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
代码图示
多态使用注意点
-
多态是方法的多态,属性没有多态。
-
父类和子类,有联系,会有一种类型转化异常。 ClassCastException!
-
多态使用的条件:有继承关系、方法需要重写、父类引用指向子类对象!( Father f1 = new Son(); )
-
不能使用多态的情况
-
static 方法 ,是属于类的,不属于实例。
-
final 常量。
-
private 方法。
-
instanceof关键字
-
是用来判断类与类之间的关系,如果存在父子类关系,返回true;不是父子类关系,返回false;毫无关系,则编译报错。
-
代码图示及演示
package com.Hrstudy.oop.demo07;
public class Application {
public static void main(String[] args) {
//测试 instanceof
//线路有三条
//Object > String
//Object > Person > Teacher
//Object > Person > Student
Object Object = new Student();
System.out.println(Object instanceof Student);//true
System.out.println(Object instanceof Person);//true
System.out.println(Object instanceof Object);//true
System.out.println(Object instanceof Teacher);//false
System.out.println(Object instanceof String);//false
System.out.println("=============================");
Person person = new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
//System.out.println(person instanceof String);//编译报错 Person 和 String 毫无瓜葛
System.out.println("=============================");
Student student = new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
//System.out.println(student instanceof Teacher);//编译报错
//System.out.println(student instanceof String);//编译报错
System.out.println("=============================");
}
}
父类与子类之间类型转换(主要讲解父类与子类的强制转换)
- 代码图示及演示
package com.Hrstudy.oop.demo08;
public class Application {
public static void main(String[] args) {
//父类和子类之间类型的转换
//子类转父类 低 --> 高 自动转换
Person s1 = new Student();
s1.run(); //可以使用从父类继承的方法
//s1.go(); //不可以使用子类中自己的方法,级别太高,需要向下转型(强制转换)
//为了使用子类中自己的方法,需要将 s1 这个对象类型由Person类型转换为Student类型!
Student s2 = (Student) s1;
s2.run(); //可以使用从父类继承的方法
s2.go(); //可以使用子类中自己的方法
//上面的强制转换可以简写为
((Student) s1).go(); //将s1强制转换后,调用子类自身的 go 方法
}
}
- 父类与子类之间类型转换小结
- 父类引用指向子类的对象 Father f1 = new Son ( );
- 把子类转换为父类,为向上转型。
- 把父类转换为子类,为向下转型,需要强制转换。
- 引用数据类型进行强制转换时,子类可能会丢失一些自己的方法,普通类型(int,float等)进行强制转换也可能会丢失数据的精度。
- 进行强制转换是为了方便方法的调用,减少重复的代码,更加简洁。
static关键字总结
代码案例
-
静态变量使用
- 静态变量通过 类名.静态变量名 即可使用。
- 非静态变量需要实例化一个对象之后 通过 对象名.非静态变量名 使用。
- 静态变量也可以通过非静态变量的方式使用。
package com.Hrstudy.oop.demo09; public class Student { //静态变量 多线程使用较多 private static int age; //非静态变量 private int score; public static void main(String[] args) { Student s1 = new Student(); System.out.println(Student.age); //静态变量通过 类名.静态变量名 即可使用 //System.out.println(Student.score); //非静态变量不可以这么使用,会报错 System.out.println("======================="); System.out.println(s1.score); //非静态变量需要实例化一个对象之后 通过 对象名.非静态变量名 使用 System.out.println(s1.age); //静态变量也可以通过非静态方法使用 } }
-
静态方法使用
- 静态方法可以通过 类名.静态方法名 使用。
- 静态方法也可以直接调用,主方法中直接使用 静态方法名。
- 非静态方法需要先实例化,再用 对象名.非静态变量名 使用。
- 静态方法也可以通过非静态方法的方式使用。
package com.Hrstudy.oop.demo09; public class Teacher { //非静态方法 public void run(){ System.out.println("非静态方法"); } //静态方法 public static void go(){ System.out.println("静态方法"); } public static void main(String[] args) { go(); //静态方法可以直接调用 Teacher.go(); //静态方法可以通过 类名.静态方法名 使用 Teacher teacher = new Teacher(); teacher.run(); //非静态方法需要先实例化 在调用 } }
-
静态代码块
- 静态代码块是所有代码块最先执行的,随着类的加载而加载。
- 静态代码块一般用来赋初始值。
- 静态代码块只随类的加载只执行一次。
package com.Hrstudy.oop.demo09; public class CodeBlack { //代码块讲解 //1.匿名代码块 (程序中用来赋 初始值 的) { System.out.println("匿名代码块"); } //2.静态代码块 static { System.out.println("静态代码块"); } //3.构造方法 public CodeBlack(){ System.out.println("构造方法"); } public static void main(String[] args) { CodeBlack c1 = new CodeBlack(); //第一次运行输出 静态代码块 匿名代码块 构造方法 System.out.println("==================================="); CodeBlack c2 = new CodeBlack(); //第二次执行输出 匿名代码块 构造方法 //静态代码块只随类的加载执行一次 } }
-
静态导入包
- 静态导入包可以具体到包中的哪一个方法。
- 声明了静态导入包后,在主方法中,可以直接使用方法名来调用该方法。
package com.Hrstudy.oop.demo09; //静态导入包 import static java.lang.Math.random; //具体到方法名 import static java.lang.Math.PI; public class StaticPackage { public static void main(String[] args) { //静态导入包 //例如调用数学函数的随机数 //正常方法 double a = Math.random(); System.out.println(a); //静态导入包的方法 System.out.println(random()); //直接使用方法名,输出随机数 System.out.println(PI); //直接输出圆周率 } }
抽象类和接口
抽象类
- 抽象类就是用abstract修饰的类;同理,抽象方法就是用abstract修饰的方法。
- 不能去 new 这个抽象类,只能靠子类去实现它,形成约束!
- 抽象类中可以写普通的方法,但是,抽象方法只能写在抽象类中。
- 继承了抽象类的子类,需要实现父类中所有抽象方法;除非子类也是抽象类。
接口
-
接口就是规范(专业的约束),在接口中无法写方法。约束和实现是分离的(大多数程序都是面向接口编程)。
-
接口的本质是契约,就像我们人间的法律一样,制定好后大家一起遵守。
-
接口与实现接口的规则
- 定义接口的关键词是 interface。
- 在接口中定义的方法都是抽象的,默认修饰词都是 public abstract;在接口中定义的属性,默认修饰词都是 public static final,被视为常量(需要大写属性名)。
- 接口中定义的方法不能被实例化(和抽象类一样),接口中没有构造方法,无法new。
- 实现接口类的关键词是 implements,接口可以实现多继承(java类只有单继承)。
- 实现接口类必须要重写接口中的方法。
-
接口代码图示
内部类
定义
-
内部类就是一个在类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就称之为内部类,而A类相对B类来说就是外部类了。
//相对于内部类的外部类
public class People{
//内部类
public class love{ }
}
-
内部类的注意点
内部类可以访问到外部类的静态成员,但是不能访问外部类的实例成员,因为实例成员是由对象创建的,内部类无法找到,除非在内部类中new一个对象,进行间接的访问。
-
内部类的使用场景
(1)当一个事物的内部,还有一部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计。
(2)内部类通常可以方便访问外部类成员,包括私有的成员。
(3)内部类提供了更好的封装性,内部类本身可以用private、protected等修饰,封装性可以做更多的控制。
分类
成员内部类(静态,非静态)
-
1.成员内部类
-
成员内部类是定义在外部类的成员位置,并且没有static的修饰。
-
**成员内部类可以直接访问外部类的所有成员,包括私有权限的。**因为,内部类的实例对象是在外部类创建之后而创建的。
-
成员内部类可以添加任意访问修饰符,因为它的地位就是一个成员。
//外部类 public class outer{ //成员内部类 public class inner{ } }
-
成员内部类的作用
(1)作为外部类的成员
- 可以调用外部类的结构。
- 可以被static修饰。
- 可以被四种不同的权限修饰。
(2)作为一个类
- 类中可以定义属性、方法、构造器等。
- 可以被final修饰,表示此类不能被继承;不使用final,就可以被继承。
- 可以被abstract修饰。
-
成员内部类创建对象的格式:
外部类名.内部类名 对象名 = new 外部构造器.new内部构造器( );
outer.inner in = new outer().new inner();
-
-
2.静态内部类(有static修饰)
-
静态内部类中可以定义静态成员和实例成员。外部类以外的其他类需要通过完整的 类名.静态内部类名.静态变量名 来访问。
public class Outer { //静态内部类 static class Inner { int a = 0; // 实例变量a static int b = 0; // 静态变量 b } } //其他类 class OtherClass { Outer.Inner oi = new Outer.Inner(); int a2 = oi.a; // 访问实例成员 int b2 = Outer.Inner.b; // 访问静态成员
}
* 静态内部类可以直接访问外部类的静态成员,**如果访问外部类的实例成员,则需要通过外部类的实例去访问。** ```java public class Outer { int a = 0; // 实例变量 static int b = 0; // 静态变量 static class Inner { Outer o = new Outer; int a2 = o.a; // 访问实例变量 int b2 = b; // 访问静态变量 } }
-
静态内部类创建对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
outer.inner in = new outer.inner();
-
局部内部类(方法内,代码块内,构造器内)
- 局部内部类是放在方法、代码块、构造器等执行体中。
//外部类
public class Test {
//当前方法
public void method() {
// 局部内部类
class Inner {
}
}
}
-
局部内部类和局部变量一样,不能使用访问控制修饰符。
-
局部内部类中不能定义static成员。
-
局部内部类中可以访问外部类的所有成员。
-
局部内部类中只可以访问 当前方法中 final 类型 的参数与变量。如果当前方法中的成员与外部类中的成员同名,则可以使用 外部类类名.this.成员名。
匿名内部类
-
匿名内部类实际上是一个没有名字的局部内部类,定义在当法中、代码块中、等。方便创建子类对象,最终目的为了简化代码编写。
-
使用匿名对象来调用,但是匿名对象没有引用变量,每次创建一个匿名对象都在堆中创建内存,都具有不同的内存地址,这样比较浪费资源。
-
格式:
People p = new People(){ public void run(){ } }; p.run(); new 类名|抽象类名|或者接口名(){ 重写方法; };
-
体验使用匿名内部类的对比:
//之前学习多态的方法,在子类中重写父类的方法。 public static void main(String[] args) { //父类 class People{ void run() { } } //子类 class Student extends People{ @Override public void run() { System.out.println("小学生跑的慢"); } } People p = new Student(); p.run(); } //输出结果: //小学生跑的慢
//使用了匿名内部类简化了代码的写法 public static void main(String[] args) { class People{ void run() { } } //匿名内部类 People p = new People(){ void run(){ System.out.println("小学生跑的慢"); } }; p.run(); } //输出结果: //小学生跑的慢
代码演示
package com.Hrstudy.oop.demo12;
//一个java类中可以有多个class类,但是只能有一个 public class
public class Outer {
private int id = 10;
public void out(){
System.out.println("这是外部类的方法");
}
//1.成员内部类
public class Inner{
public void in(){
System.out.println("这是成员内部类的方法");
}
//获取外部类的私有属性
public void getID() {
System.out.println(id);
}
}
//2.静态内部类
static class Inners{
public void in(){
System.out.println("这是静态内部类的方法");
}
}
//3.局部内部类
//可以在局部内部类中new 一个对象来调用
public void method(){
class Inner{
public void in(){
System.out.println("这是局部内部类的方法");
}
}
Inner in = new Inner();
in.in();
}
}
//一个java类中可以有多个class类,但是只能有一个 public class
class one {
}
//main方法
package com.Hrstudy.oop.demo12;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过这个外部类来实例化内部类
//成员内部类
Outer.Inner inner = outer.new Inner();
inner.in(); //这是成员内部类的方法
inner.getID(); //10
//静态内部类
Outer.Inners inners = new Outer.Inners();
inners.in(); //这是静态内部类的方法
//局部内部类
outer.method(); //这是局部内部类的方法
}
}
//匿名内部类演示
package com.Hrstudy.oop.demo12;
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
//匿名对象
new Apple().eat();
UserService userService = new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
//创建接口
interface UserService{
void hello();
}
lass
class one {
}
//main方法
package com.Hrstudy.oop.demo12;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过这个外部类来实例化内部类
//成员内部类
Outer.Inner inner = outer.new Inner();
inner.in(); //这是成员内部类的方法
inner.getID(); //10
//静态内部类
Outer.Inners inners = new Outer.Inners();
inners.in(); //这是静态内部类的方法
//局部内部类
outer.method(); //这是局部内部类的方法
}
}
//匿名内部类演示
package com.Hrstudy.oop.demo12;
public class Test {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
//匿名对象
new Apple().eat();
UserService userService = new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
//创建接口
interface UserService{
void hello();
}