Java基础篇三_面向对象
2021/4/11 14:27:35
本文主要是介绍Java基础篇三_面向对象,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Java面向对象
面向过程&面向对象
- 面向过程
步骤清晰简单,第一步做什么,第二步做什么…
面向过程适合处理一些较为简单的问题
-
面向对象
物以类聚,分类的思维模式,思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考,最后,才对某个分类下的细节进行面向过程的思索
面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
-
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统,但是,具体到微观操作,仍然需要面向过程的思路去处理
什么是面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象编程的本质就是:以类的方式组织代码,以对象的形式组织(封装)数据
-
抽象:编程的最重要的思想
-
三大特性:
封装
继承
多态
- 从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象
- 从代码运行角度考虑是先有类后有对象,类是对象的模板
类与对象的关系
-
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物
动物、植物、手机、电脑…
Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体的事物应该具备的特点和行为
-
对象是抽象概念的具体实例
张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例
能够体现出特点,展现出功能的是具体的实例,而不是一个抽象的概念
package com.keke.oop.demo01; /** * @ClassName: Student * @Description: 学生类 * @Author: keke * @Date: 2021/4/7 */ public class Student { //属性:字段 String name; // 默认null int age; // 默认0 //方法 public void study() { System.out.println(this.name + "在学习"); } }
package com.keke.oop.demo01; /** * @ClassName: Application * @Description: 一个项目应该只存在一个main方法 * @Author: keke * @Date: 2021/4/7 */ public class Application { public static void main(String[] args) { //类:抽象的,实例化 //student对象就是一个Student类的具体实例 Student student = new Student(); Student xiaoming = new Student(); Student xiaohong = new Student(); xiaoming.name = "小明"; xiaoming.age = 3; System.out.println(xiaoming.name); System.out.println(xiaoming.age); xiaohong.name = "小红"; xiaohong.age = 3; System.out.println(xiaohong.name); System.out.println(xiaohong.age); } }
创建与初始化对象
-
使用new关键字创建对象
-
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的,并且构造器有以下两个特点:
1、必须和类的名字相同
2、必须没有返回类型,也不能写void
-
构造器必须掌握
package com.keke.oop.demo02; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/7 */ public class Person { String name; //一个类即使什么都不写,它也会存在一个构造方法,称为空参构造方法,若重新定义一个构造方法,空参构造方法将被覆盖 //此时若需要构造方法,必须显示定义 //构造方法:1.和类名相同 2.没有返回值 public Person() { } public Person(String name) { this.name = name; } }
package com.keke.oop.demo02; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/7 */ public class Application { public static void main(String[] args) { Person person = new Person("keke"); System.out.println(person.name); } }
Java简单内存图
代码:
package com.keke.oop.demo03; /** * @ClassName: Pet * @Description: * @Author: keke * @Date: 2021/4/7 */ public class Pet { public String name; public int age; public void shout() { System.out.println("叫了一声"); } }
package com.keke.oop.demo03; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/7 */ public class Application { public static void main(String[] args) { Pet dog = new Pet(); dog.name = "旺财"; dog.age = 3; dog.shout(); System.out.println(dog.name); System.out.println(dog.age); Pet cat = new Pet(); } }
内存图:
简单小结类和对象
1.类与对象
类是一个模板:抽象,对象是一个实例:具体
2.方法
定义、调用
3.对应的引用
基本类型(8种) 引用类型:除了8大基本类型
对象是通过引用操作的:栈—>堆(地址)
4.属性:即字段Field/成员变量
默认初始化:
数字:0 0.0
char:u0000
boolean:false
引用:null
定义:修饰符 属性类型 属性名 = 属性值
5.对象的创建和使用
- 必须使用new关键字创建对象(调用构造器) Person keke = new Person()
- 对象的属性 keke.name
- 对象的方法 keke.sleep()
6.类
静态的属性 即属性
动态的行为 即方法
封装
- 该露的露,该藏的藏
我们程序设计要追求”高内聚,低耦合“。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉,低耦合:仅暴露少量的方法
给外部使用
-
封装(数据的隐藏)
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
-
记住这句话就行了:属性私有,get/set
package com.keke.oop.demo04; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/8 */ public class Student { //属性私有 //名字 private String name; //学号 private int id; //性别 private char sex; //年龄 private int age; //提供一些可以操作这个属性的方法 //即提供一些public的get、set方法 //get:获得这个数据 public String getName() { return this.name; } //set:给这个数据设置值 public void setName(String name) { this.name = name; } //alt + insert:自动生成set和get 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) { this.age = 3; //不合法的年龄都等于3岁 } else { this.age = age; } } }
package com.keke.oop.demo04; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/8 */ public class Application { public static void main(String[] args) { /* * 总结:封装的意义 * 1.提高程序的安全性,保护数据 * 2.隐藏代码的实现细节 * 3.统一接口 * 4.系统可维护性增加了 * */ Student s1 = new Student(); s1.setName("keke"); System.out.println(s1.getName()); s1.setAge(999); //不合法,可以在set方法中设置规则 System.out.println(s1.getAge()); } }
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
-
extends的意思是”扩展“,子类是父类的扩展
-
Java中类只有单继承,没有多继承,即一个儿子只能有一个爸爸,但一个爸爸可以有多个儿子
-
继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)子类继承父类,使用关键字extends来表示
-
子类和父类之间,从意义上讲应该具有”is a“的关系
-
在Java中,所有的类都默认直接或者间接继承Object
-
final关键字修饰的类不可以被继承,断子绝孙
-
object类
-
super关键字
-
方法重写
父类:Person
package com.keke.oop.demo05; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/8 */ // Person 人:父类 // 在Java中,所有的类都默认直接或者间接继承Object public class Person { /* * public则所有的子类以及其它类都可以使用 * 若换成private则其子类以及其它类均不能使用 * 若换成protected则可以被与父类在不同包下的子类和同一包下的其他类使用 * 若换成default(即不写也不能写)则可以被同与父类在同一包下的子类以及其它类使用 * */ protected int money = 10_0000_0000; public void say() { System.out.println("说了一句话"); } }
子类:Student、Teacher
package com.keke.oop.demo05; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/8 */ // Student is 人:子类 / 派生类 // 子类继承父类,就会拥有父类的全部方法 public class Student extends Person { // ctrl + h:查看本类的继承关系 }
package com.keke.oop.demo05; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/8 */ //Teacher is 人:子类 / 派生类 public class Teacher extends Person { }
测试类:Application
package com.keke.oop.demo05; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/8 */ public class Application { public static void main(String[] args) { Student student = new Student(); student.say(); System.out.println(student.money); Person person = new Person(); int a = person.money; } }
super关键字
首先需要了解不同访问修饰符修饰属性、方法带来的效果:
public:所有的子类以及其它类都可以使用
private:其子类以及其它类均不能使用
protected:可以被与父类在不同包下的子类和同一包下的子类以及其他类使用
default(即不写也不能写):可以被同与父类在同一包下的子类以及其它类使用
具有可见:https://blog.csdn.net/qq_46601365/article/details/115561447
1、一个类创建默认含有一个无参构造方法,若显示一个有参构造方法,无参构造方法将被覆盖
2、使用super关键字调用构造方法只能被本类的构造方法调用,不能被本类的成员方法以及静态方法调用
3、super只能出现在子类的方法或者构造方法中
4、子类无参构造方法和有参构造方法在使用Alt+ins创建过程中都会提示调用父类的构造方法,且必须调用父类的构造方法,若选父类的无参构造,不显示,默认在第一行,显示也必须在第一行;若选父类的有参构造,显示,默认在第一行。有且只能有一个。即有子必有父
5、子类构造方法不能同时调用父类和自己的构造方法,即super和this不能同时调用构造方法,即子类构造方法不能调用自己的构造方法和构造方法
VS this
代表对象不同:this:本身调用者这个对象 super:父类对象的应用
前提:this:没有继承也可以使用 super:只能在继承条件才可以使用
构造方法:this():调用本类的构造方法 super:调用父类的构造方法
父类:
package com.keke.oop.demo06; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/8 */ // Person 人:父类 public class Person { protected String name = "keke"; //注意:私有的东西无法被继承 public void print() { System.out.println("Person"); } public Person() { } public Person(String name) { //this(); this.name = name; System.out.println("Person无参执行了"); } }
子类:
package com.keke.oop.demo06; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/8 */ // Student is 人:子类 / 派生类 // 子类继承父类,就会拥有父类的全部方法 public class Student extends Person { private String name = "qingjiang"; public void test(String name) { System.out.println(name); //柯柯 System.out.println(this.name); //qingjiang System.out.println(super.name); //keke } /*public void print() { System.out.println("Student"); }*/ public void test1() { print(); //Student this.print(); //Student super.print(); //Person // super(); //Call to 'super()' must be first statement in constructor body:构造方法只能被本类的构造方法调用,且必须在第一行 } public Student() { //隐藏代码:调用了父类的无参构造 //注意:如果显示super(),调用父类的无参构造,必须写在最前面,否则会报错 // super(); //子类无参构造方法不能同时调用父类和自己的构造方法,即super和this不能同时调用构造方法,即子类无参构造方法不能调用自己的无参构造方法和有参构造方法 // this(); //父类不含无参构造时,子类构造必须显示调用父类的有参构造 // super("aaa"); System.out.println("Student无参执行了"); } public Student(String name, String name1) { super(name); this.name = name1; } public Student(String name) { //隐藏代码:调用了父类的无参构造 //注意:如果显示super(),调用父类的无参构造,必须写在最前面,否则会报错 // super(); //子类无参构造方法不能同时调用父类和自己的构造方法,即super和this不能同时调用构造方法,即子类无参构造方法不能调用自己的无参构造方法和有参构造方法 // this(); //父类不含无参构造时,子类构造必须显示调用父类的有参构造 super("aaa"); this.name = name; System.out.println("Student有参执行了"); } }
package com.keke.oop.demo06; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/8 */ //Teacher is 人:子类 / 派生类 public class Teacher extends Person { }
测试类:
package com.keke.oop.demo06; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/8 */ public class Application { public static void main(String[] args) { Person person = new Person("aaa"); person.print(); // student.test("柯柯"); // student.test1(); } }
方法重写
重写前提:
1、需要有继承关系,子类重写父类的方法
2、方法名必须相同
3、参数列表必须相同
4、修饰符:范围可以扩大但不能缩小:public>protected>default>private
5、抛出的异常:范围可以缩小但不能扩大:例:ClassNotFoundException --> Exception(大)
注意点:
1、重写都是方法的重写,与属性无关
2、重写只针对非静态方法,static、final、private修饰的方法无法重写
3、子类的方法和父类必须一致,方法体不同!
为什么需要重写:
父类的功能,子类不一定需要,或者不一定满足
父类:
package com.keke.oop.demo07; /** * @ClassName: B * @Description: * @Author: keke * @Date: 2021/4/10 */ //重写都是方法的重写,与属性无关 public class B { public void test() { System.out.println("B=>test()"); } public static void testStatic() { System.out.println("B=>testStatic()"); } }
子类:
package com.keke.oop.demo07; /** * @ClassName: A * @Description: * @Author: keke * @Date: 2021/4/10 */ public class A extends B { //alt + ins:选择override:重写父类的方法 @Override //注解:有功能的注释! Override:重写 public void test() { // super.test(); //默认调用父类的方法 System.out.println("A=>test"); //重写自己的代码 } public static void testStatic() { System.out.println("A=>testStatic()"); } }
测试类:
package com.keke.oop.demo07; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Application { public static void main(String[] args) { A.testStatic(); //A=>testStatic() B.testStatic(); //B=>testStatic() A a = new A(); a.test(); //A=>test() //父类的引用指向子类对象 B b = new A(); b.test(); //A=>test() /* * 结论:重写只针对非静态方法 * */ } }
多态
-
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型是确定,但可以指向对象的引用的类型有很多
-
多态存在的条件
有继承关系
子类重写父类的方法
父类引用指向子类对象
-
注意:多态是方法的多态,属性没有多态
-
instanceof
-
总结:
-
对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
例:父类的引用指向子类的对象后,若调用子类特有的方法则报错,但可以强制转换为子类对象再调用
-
父类的引用指向子类的对象后,该对象调用重写的方法执行的是子类重写后的方法
-
补充:
-
编译时的多态:方法重载(overload)
-
运行时的多态:方法重写(override)
-
运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
父类:
package com.keke.oop.demo08; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Person { public void run() { System.out.println("run"); } }
子类:
package com.keke.oop.demo08; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Student extends Person { @Override public void run() { System.out.println("son"); } public void eat() { System.out.println("eat"); } }
测试类:
package com.keke.oop.demo08; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Application { public static void main(String[] args) { //一个对象的实际类型是确定的 // new Student(); // new Person(); //但是可以指向的引用类型就不确定了 //Student能调用的方法都是自己或者继承父类的 Student s1 = new Student(); //Person 父类型 可以指向子类,但是不能调用子类独有的方法 Person s2 = new Student(); //父类的引用指向子类的对象 Object s3 = new Student(); s2.run(); //子类重写了父类的方法,执行子类的方法 s1.run(); //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大 // s2.eat(); //报错 ((Student) s2).eat(); //强制转换后可以调用 s1.eat(); } }
instanceof
instanceof的作用:判断一个对象是什么类型,用于引用类型的判断
**X instanceof Y: **
是否能编译通过:取决于X和Y是否存在父子关系,存在则编译通过,不存在则编译失败
是否为true:取决于X指向的对象类型是不是Y的子类型或者Y类型本身
父类:
package com.keke.oop.demo09; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Person { public void run() { System.out.println("run"); } }
子类:
package com.keke.oop.demo09; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Student extends Person { }
package com.keke.oop.demo09; /** * @ClassName: Teacher * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Teacher extends Person { }
测试类:
package com.keke.oop.demo09; /** * @ClassName: Teacher * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Application { public static void main(String[] args) { //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); //编译报错 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("=================================="); Person person1 = new Person(); System.out.println(person1 instanceof Student); //false System.out.println(person1 instanceof Person); //true System.out.println(person1 instanceof Object); //true System.out.println(person1 instanceof Teacher); //false // System.out.println(person1 instanceof String); //false } /* * System.out.println(X instanceof Y); * 是否能编译通过:取决于X和Y是否存在父子关系,存在则编译通过,不存在则编译失败 * 是否为true:取决于X指向的对象类型是不是Y的子类型或者Y类型本身 * */ }
引用类型转换
1、引用类型之间的转换:就是父类与子类之间的转换
2、前提条件:父类引用指向子类对象
-
向下转型(强制转换):父 -> 子
-
向上转型:子 -> 父,会丢失一些子类特有的方法
3、作用:方便方法的调用,减少重复的代码
父类:
package com.keke.oop.demo10; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Person { public void run() { System.out.println("run"); } }
子类:
package com.keke.oop.demo10; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Student extends Person { public void go() { System.out.println("go"); } }
测试类:
package com.keke.oop.demo10; /** * @ClassName: Teacher * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Application { public static void main(String[] args) { //引用类型之间的转换:父 子 //高 -> 低 Person obj = new Student(); // obj.go(); //父类引用指向子类对象:无法调用子类特有的方法 //将obj这个对象转换为Student类型,就可以使用Student类特有的方法 //向下转型 ((Student) obj).go(); //子类转换为父类,可能会丢失自己本来的一些方法 Student student = new Student(); student.go(); Person person = student; // 低 -> 高 向上转换 // person.go(); //报错 } }
static关键字
- 作用:
1、修饰类中的成员变量 -> 类变量:可以通过类名访问,不需要new一个对象
2、修饰类中的成员方法(非静态方法)-> 静态方法:可以通过类名访问,不需要new一个对象
package com.keke.oop.demo11; /** * @ClassName: Student * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Student { private static int age; //静态变量 private double score; //成员变量 / 非静态变量 public void run() { // go(); //成员方法中可以直接调用静态方法 } //成员方法 / 非静态方法 public static void go() { // run(); //报错 静态方法中不能直接调用成员方法 // Student student = new Student(); // student.run(); } //静态方法 public static void main(String[] args) { Student s1 = new Student(); // System.out.println(s1.age); System.out.println(Student.age); //推荐使用类名调用 System.out.println(s1.score); // System.out.println(Student.score); //报错 new Student().run(); Student.go(); go(); //因为在本类中调用,可以省略类名 } }
3、静态代码块:类加载即执行,只执行一次
package com.keke.oop.demo11; /** * @ClassName: Person * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Person { //2 { //匿名代码块(不建议):没有名字,程序在执行的时候不能主动调用,创建本类对象时自动创建,在构造器之前 //作用:赋初值 System.out.println("匿名代码块"); } //1 static { //静态代码块(建议):类加载即执行,只执行一次 System.out.println("静态代码块"); } //3 public Person() { System.out.println("构造方法"); } public static void main(String[] args) { Person person = new Person(); //静态代码块 匿名代码块 构造方法 System.out.println("================="); Person person1 = new Person(); //静态代码块 匿名代码块 构造方法 } }
4、静态导入包:将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便
package com.keke.oop.demo11; //静态导入包 import static java.lang.Math.random; import static java.lang.Math.PI; /** * @ClassName: Test * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Test { public static void main(String[] args) { // System.out.println(Math.random()); System.out.println(random()); //使用静态导入包后,不用写Math,直接写random()方法即可 System.out.println(PI); } }
-
注意:
1、成员方法中可以直接调用静态方法,但是静态方法中不能直接调用成员方法,需要new一个对象通过对象调用使。因为用static关键字修饰的属性和方法在内存中是跟着类一起加载,所以可以通过类名直接调用
抽象类
-
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
-
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
-
抽象类,不能使用new关键字来创建对象,它用来让子类继承
-
抽象方法,只有方法的声明,没有方法的实现,它用来让子类实现
-
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
-
存在的意义:把一些类的公共方法给抽象定义在一起,然后创建这些类都继承抽象类,避免的这些类公共方法的重复编写,简化了代码的开发,提高开发效率和扩展性
-
能否称为架构师最重要的一点是是否具有抽象的思维!
抽象类:
package com.keke.oop.demo12; /** * @ClassName: Action * @Description: * @Author: keke * @Date: 2021/4/10 */ //abstract修饰类代表是抽象类 类只能单继承,接口可以多继承 public abstract class Action { //abstract,抽象方法,只有方法名字,没有方法的实现 public abstract void doSomething(); //特点: //1.不能使用new关键字来创建对象,它用来让子类继承,相当于一个约束 //2.抽象类中可以写普通的方法 //3.抽象方法必须在抽象类中 public void hello() { System.out.println("普通的方法"); } }
子类:
package com.keke.oop.demo12; /** * @ClassName: A * @Description: * @Author: keke * @Date: 2021/4/10 */ //子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类 public class A extends Action { @Override public void doSomething() { } }
测试类:
package com.keke.oop.demo12; /** * @ClassName: Application * @Description: * @Author: keke * @Date: 2021/4/10 */ public class Application { public static void main(String[] args) { // new Action(); //报错 Action a = new A(); } }
思考:抽象类不能new,那么它是否存在构造器?
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // public abstract class Action { public Action() { } public abstract void doSomething(); public void hello() { System.out.println("aaa"); } }
解答:通过javac Action.java命令生成的Action.class文件可以看出抽象类存在构造器
接口
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有
-
接口:只有规范,自己无法写方法 专业的约束,实现约束和实现分离:也就是面向接口编程
-
接口就是规范,定义的是一组规则,体现了现实世界中,“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须干坏人;如果你是坏人,则必须欺负好人。
-
接口的本质就是契约,就像我们人间的法律一样,制定好后大家都遵守
-
OOP的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如:java、c++、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象
接口的作用:
1、接口本质是一个约束、规范
2、定义了一些方法,让不同的人是实现,比如十个类都是实现了一个接口,但是实现方式不同
3、接口中的所有定义的方法其实都是抽象的,默认自带public abstract
4、接口中的所有定义的属性都是静态常量,默认自带public static final,不能改变,但一般不在接口中定义属性
5、接口不能被实例化,因为接口中没有构造方法
6、一个类可以实现多个接口,使用implements关键字实现
7、实现接口必须重写接口中全部的抽象方法
Java8新特性:
1、接口中可以定义静态方法(必须使用static修饰),不是抽象的,具体实现的,可以直接使用接口名称调用
2、接口中可以定义默认方法(必须使用default修饰),默认方法是非静态方法,非静态方法只能通过对象调用,但是接口是不能创建对象的名故而我们需要子类来实现接口
此外,值得注意的是:面向对象中,java只是支持单继承,不支持多继承,一个类只能继承一个类,
(1)如果多个接口定义了同样的静态方法,
即使存在实现类,同时实现多个接口,仍然是不可使用实现类名调用接口的方法
(2)如果多个接口定义了同样的默认方法
实现类实现多个接口时,必须重写掉默认方法,否则编译失败。
接口和抽象类的区别
注:抽象方法:必须被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
1、实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
2、接口中可以定义抽象方法(默认被public abstract修饰,不能修改)、[默认方法(必须使用default修饰)、静态方法](默认被public修饰,不能更改),可以定义属性但都是静态常量,默认自带public static final,不能改变,必须赋初值,不能实现类被修改;
抽象类中可以定义抽象方法、静态方法、成员方法、静态属性和非静态属性,抽象方法必须显示被abstract修饰。
3、抽象类的优点:如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个 新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点
接口的优点:一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。(使用抽象类,那么继承这个抽象类的子类类型就比较单一,因为子类只能单继承抽象类;而子类能够同时实现多个接口,因为类型就比较多。接口和抽象类都可以定义对象,但是只能用他们的具体实现类来进行实例化。) 从该点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。
4、结合3点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍然由Java接口承担,但是同时给出一个Java 抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类,也就是说在层次结构中,Java 接口在最上面,然后紧跟着抽象类,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模式”。在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。(A extends AbstractB implements interfaceC,那么A即可以选择实现(@Override)接口interfaceC中的方法,也可以选择不实现;A即可以选择实现(@Override)抽象类AbstractB中的方法,也可以选择不实现)
缺省适配模式的好处:假设设计了一个接口,但实现这个接口的子类并不需要实现接口中的全部方法,也就是说,接口中的方法过多,对于某些子类是多余的,我们不得不浪费的写上一个空的实现;这时可以定义一个抽象类去实现该接口,对接口中所有的抽象方法进行空的实现,以便后续具体子类只需继承该抽象类按照需要重写自己想要的方法进行具体实现,另外,抽象类中还可以定义抽象方法,也可以实现对个接口,将抽象类和接口的优势发挥到了极致!
接口:
package com.keke.oop.demo13; /** * @ClassName: TimeService * @Description: * @Author: keke * @Date: 2021/4/10 */ public interface TimeService { void timer(); }
package com.keke.oop.demo13; /** * @ClassName: UserService * @Description: * @Author: keke * @Date: 2021/4/10 */ //interface 定义的关键字,接口都需要有实现类 public interface UserService { //接口中的所有定义的属性都是静态常量,默认自带public static final,但一般不在接口中定义属性 int AGE = 99; //接口中的所有定义的方法其实都是抽象的,默认自带public abstract void add(String name); void delete(String name); void update(String name); void query(String name); static void aaa() {}; }
抽象类:
package com.keke.oop.demo13; /** * @ClassName: UserService * @Description: * @Author: keke * @Date: 2021/4/10 */ //类可以实现接口 implements 接口 //实现了接口的类就必须重写接口中所有的抽象方法 //可以利用接口实现多继承 public abstract class UserServiceAbstract implements UserService, TimeService{ @Override public void timer() { } @Override public void add(String name) { } @Override public void delete(String name) { } @Override public void update(String name) { } @Override public void query(String name) { } }
实现类:
package com.keke.oop.demo13; /** * @ClassName: UserServiceImpl * @Description: * @Author: keke * @Date: 2021/4/11 */ public class UserServiceImpl extends UserServiceAbstract { @Override public void add(String name) { System.out.println("add"); } @Override public void delete(String name) { System.out.println("delete"); } }
内部类
- 内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就称为内部类,而A类相对于B类来说就称为外部类
1、成员内部类
2、静态内部类
3、局部内部类
4、匿名内部类
package com.keke.oop.demo14; /** * @ClassName: Outer * @Description: * @Author: keke * @Date: 2021/4/11 */ public class Outer { private int id = 10; private static int id2 = 20; private void out() { System.out.println("这是外部类的方法"); } private static void out2() { System.out.println("这是外部类的方法2"); } public void test() { System.out.println("用于测试匿名内部类的方法"); } //成员内部类 public class Inner { public void in() { System.out.println("这是成员内部类的方法"); } //获得外部类的私有属性、私有方法~ public void getID() { System.out.println(id); out(); System.out.println(id2); out2(); } } //静态内部类 public static class Inner2 { public void in() { System.out.println("这是静态内部类的方法"); } //只能获得外部类的静态私有属性、静态私有方法~,因为静态属性、方法、类会先实例化 public void getID() { // System.out.println(id); // out(); System.out.println(id2); out2(); } } //局部内部类:写在外部类的方法中 public testInner3 method() { class Inner3 implements testInner3 { //这里不能有访问修饰符 public void in() { System.out.println("这是局部内部类的方法"); } } return new Inner3(); } } //一个Java类中可以有对个class类,但是只能有一个public class /*public*/ class A { public static void main(String[] args) { } } interface testInner3 { void in(); }
测试类:
package com.keke.oop.demo14; /** * @ClassName: Applicaiton * @Description: * @Author: keke * @Date: 2021/4/11 */ public class Application { public static void main(String[] args) { Outer outer = new Outer(); //通过这个外部类来实例化内部类~ Outer.Inner inner = outer.new Inner(); inner.in(); inner.getID(); System.out.println("======================="); //调用局部内部类的方法 testInner3 inner3 = outer.method(); inner3.in(); //这是局部内部类的方法 System.out.println("======================="); //匿名内部类:没有名字初始化类,不用将实例保存到变量中 new Outer().test(); //常用于实现接口 new UserService() { @Override public void hello() { } }; } } interface UserService { void hello(); }
这篇关于Java基础篇三_面向对象的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-25JAVA语音识别项目项目实战入门教程
- 2024-11-25JAVA云原生项目实战入门教程
- 2024-11-25Java语音识别项目入门:新手必读指南
- 2024-11-25Java语音识别项目入门:轻松开始你的第一个语音识别项目
- 2024-11-25Java语音识别项目入门详解
- 2024-11-25Java语音识别项目教程:从零开始的详细指南
- 2024-11-25JAVA语音识别项目教程:初学者指南
- 2024-11-25Java语音识别项目教程:初学者指南
- 2024-11-25JAVA云原生入门:新手指南与基础教程
- 2024-11-25Java云原生入门:从零开始的全面指南