面向过程和面向对象的设计思想
面向过程:是一种程序设计思想。C语言
解决问题时,是按具体的实现步骤一步一步实现。
面向过程直接关注流程。
eg. 首先 开门()
其次 装大象()
最后 关门()
面向对象:是一种程序设计思想。Java、Python,C++,C#
以分类的方式进行思考和解决问题。
先对整体关系进行分类,根据不同的类深入细节的处理。
符合人类认知习惯。
-
面向对象 —> 使用对象 对象是什么 对象从何而来
-
面向对象实现:对客观事物进行分类(设计类/发现类 类是抽象的),实际使用时,需要以类为模板创建出一 个个具体的对象。
人类 —> 张三(对象)
设计一个冰箱类(抽象的)
public class 冰箱{
名字
型号
价格
开门();
装大象;
关门();
}
冰箱 bx = new 冰箱(); //bx表示这个对象,在运行时在内存中会划出一块空间,用来存储具体的对象。
bx.开门(); //具体的对象
冰箱.开门(); //错误 冰箱是一个概念
面向对象是在宏观整体上对事物之间的关系进行设计,具体实现又回归到面向过程的具体实现,两者是相辅相成的。
什么是类
类是一个模板,是对同一类事物属性(名词)和行为(动词)的抽象。
类是有地址的,包就是其地址。
类是一个模板,它是
类的结构:
-
成员变量:事物属性的描述。名词 属性
成员变量在定义是,可以对其初始化,也可以不进行初始化,java会在构造方法中对其进行默认赋值 。 引用类型值为:null int:0 float:0.0 char:‘ ’ boolean:false
局部变量需要自己对其初始化。
创建对象时,会从类中向对象中复制一份成员变量 到对象中
成员变量可以被 方法,构造方法,代码块所访问
-
成员方法:事物的行为。(可以做的事情) 动词 行为
-
构造方法:初始化对象。 有专职作用 初始化对象
-
内部类:即在类体中声明的类。
-
代码块:一段没有名称的代码。
类的定义:
- 发现类
- 发现类的共有属性(成员变量)
- 发现类的方法
以类为模板创建对象 使用对象。
万事万物(实际存在的事物 具体) 皆为对象
//Car.java
/*发现类 汽车类
* public(访问权限修饰符) class(关键字修饰类) Car(类名)
* {
* 类体
* 成员变量
* 成员方法
* 构造方法
* }*/
public class Car {
/*
定义类的成员变量 名词 属性
直接定义在类中,所以成为类的成员
可以使用java所支持的任意的数据类型 基本类型、引用类型
*/
String name; //引用类型
String color;
float price; //基本类型
/*
定义类的成员方法 动词 行为
*/
public void run(){
System.out.println(this.name+"汽车行驶");
}
public void stop(){
System.out.println(name+"汽车停止");
}
}
什么是对象
对象是类的一个实例,是以类为模板在内存中创建的实际存在的实例。
//TestCar.java
public class TestCar {
public static void main(String[] args) {
/*
类是模板,是抽象概念 就是一个定义
对象是具体,可以直接被使用的,是以类为模板,在内存中创建出来的实际存在的实例
*/
Car bm = new Car();
bm.name = "宝马";
bm.color = "黑色";
bm.price = 200000;
System.out.println(bm.name);
bm.run();
bm.stop();
Car bc = new Car();
bc.name = "奔驰";
bc.color = "黑色";
bc.price = 2043000;
System.out.println(bc.name);
bc.run();
bc.stop();
}
}
对象的创建和使用:
-
Car bm = new Car();
-
Car() 构造方法 构造方法的名字与类名相同new Car() new 关键字,创建对象。
以Car类为模板,在内存中创建一个具体实例 将类这个模板中的成员向具体对象中复制一份,每个对象中都是独一无二的。
-
Car bm/bc 声明了一个类型为Car的变量 。
-
= 将右边的对象地址赋值给左边的变量,左边的变量就在程序中表示内存中的对象。
-
同一类的每个对象有不同的成员变量存储空间。
-
同一类的每个对象共享该类的方法。
类和对象
类是一类事物的抽象概念,是一个模型。
对象是由这个模型所创造的一个个具体存在的,实实在在存在的实例。所以创建对象的过程也叫实例化对象。
现实生活中先有对象后有类,而编程时先设计类后创建对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T07lZ2qF-1642687013926)(E:\Java学习\2022-1-16java第三章面向对象1\课件\创建对象简图.png)]
构造方法
特点:构造方法名与类名相同,且没有返回值,且不需要void修饰。
作用:在构造方法中为创建的对象初始化赋值。
每次创建对象时,至少要调用一个构造方法,一个类中可以有多个构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java将会为该类提供一个默认构造方法,但是只要在一个Java类中定义了一个构造方法后,默认的无参构造方法即失效。
一个类可以有多个构造方法。
//Car.java
public class Car {
String name;
String color;
float price;
/*
类中默认的无参构造方法,默认是有的,
如果没有显示的写出来,如果定义了有参的构造方法,那么无参的就会被覆盖
以后一般情况下,定义了有参的构造方法,最好把无参显示定义出来
*/
public Car(){
//在无参构造方法中,可以使用默认值为对象中的属性赋值
//引用类型:null 整数:0 浮点:0.0 char:‘ ’ boolean:false
}
public void run(){
System.out.println(this.name+"汽车行驶");
}
public void stop(){
System.out.println(name+"汽车停止");
}
}
//TestCar.java
public class TestCar {
public static void main(String[] args) {
/*
new Car() ---> 构造方法
特点:构造方法名与类名相同,且没有返回值,且不需要void修饰
作用:在构造方法中为创建的对象初始化赋值
每次创建对象时,至少要调用一个构造方法,一个类中可以有多个构造方法
*/
Car bm = new Car();
bm.name = "宝马";
bm.color = "黑色";
bm.price = 200000;
bm.run();
}
}
方法的重载
在一个类中有多个方法名称相同的现象。
构造方法,成员方法都可以重载。
如何区分方法:
- 通过方法参数个数区分
- 通过类型区分
- 通过顺序区分
调用时,会根据不同的参数表选择对应的方法。
注意:方法重载跟方法的返回值类型没有任何关系。
重载的作用:扩展方法功能。
//构造方法
public Car(){
//在无参构造方法中,可以使用默认值为对象中的属性赋值
//引用类型:null 整数:0 浮点:0.0 char:‘ ’ boolean:false
}
public Car(String name,String color,float price){
//this表示当前正在被使用的对象
this.name = name; //此name是成员变量中的name
this.color = color;
this.price = price;
}
//构造方法
public Car(String name,String color){
this.name = name; //此name是成员变量中的name
this.color = color;
}
public Car(float price,String name,String color){
//this表示当前正在被使用的对象
this.name = name; //此name是成员变量中的name
this.color = color;
this.price = price;
}
//成员方法
public void run(){
System.out.println(this.name+"汽车行驶");
}
public void run(int speed){
System.out.println(this.name+"汽车以"+speed+"速度行驶");
}
public class TestCar {
public static void main(String[] args) {
Car bm = new Car();
bm.name = "宝马";
bm.color = "黑色";
bm.price = 200000;
bm.run();
Car bc = new Car("奔驰","红色",300000);
bc.run();
Car dz = new Car("大众","红色");
dz.run(120);
}
}
对象与引用
java中除了8种基本类型外,其余的都是引用类型。
基本类型:使用关键字声明,结构简单。
int a = 10;
引用类型:类、数组、结构……
类是符合类型,比较复杂可以定义更多的东西
Car dz = new Car("大众","红色");
Car ad = dz; //创建了2个对象 ad、dz指向相同的对象
ad.name = "奥迪";
System.out.println(ad.name); //奥迪
System.out.println(dz.name); //奥迪
System.out.println(bc.name); //奔驰
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Desjw9nZ-1642687013933)(E:\Java学习\2022-1-17java第三章面向对象2\课件\对象创建内存图.png)]
值传递与引用传递
严格来讲,参数传递只有值传递
为了基本类型和引用类型区分:分为值传递和引用传递
-
值传递特指基本类型。
方法调用时,实际参数把它的值传递给 对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。
public class TestValue { public static void main(String[] args) { int a = 10; TestValue t = new TestValue(); t.test(a); System.out.println("a:"+a); //a的值是多少? 10 } /* 基本类型就是值传递 */ public void test(int b){ System.out.println("b:"+b); //10 b = 20; } }
-
引用传递对应的是引用类型。
也称为传地址。方法调用时,实际参数是对象,这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。
public class TestValue { public static void main(String[] args) { TestValue t = new TestValue(); Car car1 = new Car(); car1.name = "奔驰"; t.test(car1); System.out.println("car1.name:"+car1.name); //大众 } /* 引用类型 传递的不是对象本身,而是传递对象的引用地址 */ public void test(Car car2){ System.out.println("car2.name:"+car2.name); //奔驰 car2.name = "大众"; } }
基本类型传递的是该数据值本身。引用类型传递的是对对象的引用,而不是对象本身 。
this关键字
this关键字代表当前正在使用的对象。
static关键字
static被称为静态,可以用来修饰类的属性,方法,代码块,内部类。
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象共享
- 可不创建对象,直接被类调用。
静态属性是类的所有对象共享的,即不管创建了多少个对象,静态属性在内存中只有一个。
public class Aodi {
/*
Aodi 他们的名字应该都叫奥迪,在定义类的时候直接对其进行赋值。
型号和价格是不一样的
*/
String model;
float price;
//static修饰的成员变量在内存中只有一份,所有对象可以共享,随着类的加载而加载,通过类名调用
static String name = "奥迪";
}
public class TestAodi {
public static void main(String[] args) {
System.out.println(Aodi.name); //可以不创建对象,直接被类调用
Aodi a8 = new Aodi();
a8.model = "a8";
a8.price = 300000;
System.out.println(Aodi.name+":"+a8.model+":"+a8.price); //可通过类名直接调用
Aodi q8 = new Aodi();
q8.model = "q8";
q8.price = 500000;
System.out.println(q8.name+":"+q8.model+":"+q8.price);
}
}
static方法可以使用对象调用,也可以直接用类名调用,建议用类名直接调用 。
在static方法内部只能访问类的static属性,不能访问类的非static属性,static属性先加载。
静态的成员随着类加载后,就可以使用了。而非静态的必须要创建对象后,才可以使用。
//static 修饰的方法是静态方法,属于类,只能使用静态的成员变量。
public static void run(){
System.out.println(name);
System.out.println(this.model); //报错 非静态创建对象后才能使用
}
//可以直接使用
Aodi.run();
代码块
代码块在类中声明,类似于没有名称的方法体。
类加载时就自动执行。
分为实例代码块,静态代码块。
- 实例代码块:在创建对象时调用。
- 静态代码块:在类加载时调用,只调用一次(因为类只加载一次)。
{
System.out.println("实例代码块1");
}
{
System.out.println("实例代码块2");
}
/*
以后,把在类加载时,就需要自动执行的内容,写在静态代码块中,只执行一次
*/
static {
System.out.println("静态代码块1");
}
static {
System.out.println("静态代码块2");
}
public class Test {
public static void main(String[] args) {
new Aodi(); //第一次创建对象,并加载类
new Aodi(); //第二次创建对象,不加载类
}
}
//静态代码块有限执行,且执行一次。
//原因:类比对象优先加载,且只加载一次
/*输出结果为:
静态代码块1
静态代码块2
实例代码块1
实例代码块2
实例代码块1
实例代码块2
*/
包
包:为了更好的管理java中的类,创建包来进行管理,用于区别类名的命名空间。
包在硬盘上来讲其实就是文件夹,但在java项目中和文件是有区别的,是作为类的路径存在的。
directory:文件夹 package:包
同一个包下,不会有两个相同的类名。
包的作用:
-
避免类重名 (区别重名类)
package Day3; //import 使用其他包中的类时,需要导入其他包中的类(同一个包的类、java.lang包不用导入) import Day2.Car; public class Test { public static void main(String[] args) { String s; //java.lang包 /* Car是类的简称 类的全称(全类名)Day2.Car */ new Car(); //Day2包里的Car new Day1.Car(); //Day2中的Car已导入,只能使用全类名 } }
-
按照不同功能管理类
eg. 控制层、数据访问层、业务逻辑处理层、公共类、配置类、工具类等
-
控制访问权限
访问权限修饰符(见下个标题)
包的命名规范:在包名中,可以使用 . 号来区分包的级别,包名一般情况下是小写 。
- 第一级 指该项目的类型,如com(商业公司),org(组织),gov(政府),edu(教育)等,
- 第二级 指项目所开发或者运行的公司名称,如:oracle,sun,huawei 等
- 第三级 指项目的名称,如:bcms,oa,erp,cms等
- 第四级 指项目模块的名称,如:bean,action,exception等
访问权限修饰符
按照不同功能管理类
控制访问权限
Java语言有四个权限访问修饰符,权限从大到小依次为:
- public :公共权限 修饰类、属性、方法。可以被任意类访问
- protected:受保护的权限 修饰属性、方法。 可以被同包类访问,如果不是同包类,必须是该类的子类才可以访问。
- default:同包权限 修饰类、属性、方法。只能被同包的类访问
- private:私有权限 修饰属性、方法。 只能在本类中访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ubA98y4u-1642687013940)(E:\Java学习\访问权限修饰符.png)]
package com.ffyc.javaoop.day3;
/*
类 只能用public 和 默认 修饰
*/
public class Demo {
/*
public 公共权限 在任何类中都可以访问到
protected 受保护权限 在自己类中,同包的其他类中,不同包的子类中可以访问到
默认权限 在自己类中,同包的其他类中
private 私有权限 只能在自己类中访问到
*/
public String pubName;
protected String proName;
String delname; //默认
private String priName;
protected void test1(){
}
protected void test2(){
}
void test3(){
}
private void test4(){
}
public void test(){
Demo d = new Demo();
d.pubName = "p";
d.proName = "po";
d.delname = "";
d.priName = "pi";
}
}
package com.ffyc.javaoop.day3.child;
import com.ffyc.javaoop.day3.Demo;
public class ChildDemo extends Demo{
public void test(){
Demo d = new Demo();
d.pubName = "a"; //只剩下公共的
ChildDemo cd = new ChildDemo(); //默认权限和私有权限仍访问不到
cd.proName = "1";
cd.pubName = "q";
}
}
package com.ffyc.javaoop.day3;
public class Demo1 {
public void test(){
Demo d = new Demo();
d.pubName = "a";
d.proName = "p";
d.delname = "d";
}
}
面向对象的三大特征
面向对象语言的三大特征:
- 封装
- 继承
- 多态
封装
封装:将某个功能封装成一个方法。eg. 写了一个工具类,定义好了几个常用的方法。
面向对象中的封装:是隐藏(访问权限修饰符 private、protected、默认、public……)将类中的某些信息不对外开放。
private String name; //属性私有化,只是封装的一种表示
封装特点:
- 隐藏类的实现细节
- 方便加入控制语句
- 只能通过规定方法访问
- 方便修改实现
package com.ffyc.javaoop.day3.pack;
public class Person {
private String name;
private int age;
public void setAge(int age){
if(age>18){
this.age = age; //this.age对象中的age
}
}
public int getAge(){
return age;
}
//setXXX方法就是为私有属性提供访问的公共方法
public void setName(String name){
//方便加入控制语句
if(name.length()>3 && name.length()<6){
this.name = name;
}
}
public String getName() {
return this.name;
}
}
package com.ffyc.javaoop.day3.pack;
public class TestPerson {
public static void main(String[] args) {
//private 将name属性隐藏,在其他类中无法访问
Person zs = new Person();
// zs.name = "fadsf";
zs.setName("fsdfj");
System.out.println(zs.getName());
}
}
单例模式:(模式就是模板,解决一种问题,就有)
单例模式解决在一个程序中,只能让一个类创建一个对象。
package com.ffyc.javaoop.day3.pack;
public class WindowDemo {
static WindowDemo windowDemo = null;
/*
将构造方法私有化,这样在其他类中就不能创建对象了
*/
private WindowDemo(){
}
//向外提供一个方法,用来创建唯一的一个对象,并返回此对象
public static WindowDemo getWindowDemo(){
//单例模式
//第一次已经创建,第二次windowDemo不为null,因而直接返回windowDemo
if(windowDemo == null){
windowDemo = new WindowDemo();
}
return windowDemo;
}
}
package com.ffyc.javaoop.day3.pack;
public class TestWindow {
public static void main(String[] args) {
// new WindowDemo(); 构造方法私有,无法创建对象
System.out.println(WindowDemo.getWindowDemo());
System.out.println(WindowDemo.getWindowDemo());
}
}
继承
继承:是面向对象程序设计不可缺少的设计思想,实现代码可重用,提高代码可扩展性的主要途径。
什么时候使用继承:
-
可以把一些共有属性行为抽取出来,创建一个父类,让其他类继承即可。
eg. 猫是动物 狗是动物 是属于同一类 大小的关系
基本语法:
- 父类 —> 子类
- 子类 extends 父类 extends Object
public class Animal extends Object{
private String name;
private int age;
public void eat(){
System.out.println(this.name+"动物吃东西");
}
//get set方法 专属用于私有属性赋值
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/*
子类继承父类 使用extends关键字
一个类只能直接继承一个父类,但是可以间接继承
*/
public class Dog extends Animal{
}
public class AngelDog extends Dog{
//扩展字节的功能,同时重复调用父类的功能
public void fly(){
System.out.println("哮天犬会飞");
}
}
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("可可");
dog.setAge(5);
System.out.println(dog.getName());
System.out.println(dog.getAge());
dog.eat();
}
}
继承是从已有的类中派生出新的类,新的类能吸收已有类的属性和行为,并能扩展新的能力。
- 在JAVA中使用extends关键字来表示继承关系。
- JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一个直接父类。
- 继承之后子类可以调用父类的所有非私有属性和非私有方法
符合is-a关系的设计,使用继承。将子类共有的属性和行为放到父类中。
继承的传递性:
- C类从B类继承,B类又从A类继承,那么C类就具有B类和A类的所有非私有属性和非私有方法。
- 当一个没有显示的继承任何一个类时,那么jvm会默认让类继承Object类,Object(java.lang包)是所有类的基类(父类/超类)。
继承中的构造方法:
-
在创建对象,调用构造方法时,子类的构造方法会默认去调用父类的构造方法。因为要先初始化父类,子类才可以去使用父类中的属性行为。
-
使用super关键字调用父类任意一个构造方法,必须写在构造方法的第一行。
-
如果子类的构造方法中没有显式地调用基类构造方法,则系统默认调用基类无参数的构造方法。
public class AngelDog extends Dog{ //扩展字节的功能,同时重复调用父类的功能 public void fly(){ System.out.println("哮天犬会飞"); } public AngelDog(){ //super表示父类,并不是父类对象 //super()调用父类构造方法,默认存在,可以不写,在子类构造方法的第一行 //如果显示的调用,必须在第一行调用 super(); System.out.println("AngelDog类无参构造"); } }
super关键字用途:
-
使用super关键字访问父类成员。
-
用super.成员变量名来引用父类成员变量。
-
用super.方法名(参数列表)访问父类的方法。
-
用super.构造方法(参数列表)访问父类构造方法。
-
注:不要把super误认为是父类对象.在创建子类对象时, 不会创建父类对象,只会将父类中的信息加载到子类对象中存储。
public AngelDog(){ //super表示父类 super(); System.out.println("AngelDog类无参构造"); super.eat(); }
方法的重写:当父类的方法实现不能满足子类需求时,可以对方法进行重写(override)。
-
重写要求:与类中的方法名一致,参数、返回值一致,访问权限等于或大于父类权限。
-
@Override 是java中的注解标签 用来标记的。作用在方法上,用来表示此方法是从父类中重写而来的,在编译期间检测语法格式。
/* 神犬吃东西实现方式与其他动物不一样,那么就可以在哮天犬类中重写(覆盖)Animal类中的eat()方法 方法重写 重写的要求:与父类中的方法名一致,参数、返回值一致 访问权限等于或大于父类权限 @Override 是java中的注解标签,用来标记的 @Override 作用在方法上,表示此方法是从父类中重写而来的,在编译期间检测语法格式。 */ @Override public void eat(){ System.out.println("哮天犬坐着吃饭"); }
多态
同一种事物,在不同时刻表现不同的状态 。
前提:要有继承(包括接口实现),要有方法重写,父类的引用指向子类对象。
//父类的引用指向子类对象
Animal dog1 = new Dog();
Animal cat1 = new Cat();
Object d = new Dog();
Dog dog = new Dog(); //Dog的引用指向Dog对象
Cat cat = new Cat();
编译期间:写代码时就是编译期间,Animal dog1 在编译期间是Animal类型。
运行期间:run运行程序。Animal dog1 在运行期间是Dog类型。
对于成员方法来讲,编译看左边,运行看右边。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JD3FJj9a-1642687013942)(E:\Java学习\2022-1-19java第三章面向对象4\课件\Snipaste_2022-01-19_11-26-24.png)]
对于静态方法来讲,编译和运行都看左边。
对于成员变量来讲,编译和运行都看左边。
多态:提高了程序的扩展性,因而子类类型向上转为父类类型。
多态存在不足:使用多态时,不能访问子类中特有的方法,因为已经向上转为父类类型,编译期间不能调用子类特有方法。
解决:需要向下转型,转型前最好判断类型,使用instanceof关键字来判断。
public class Test2 {
public static void main(String[] args) {
/*
创建一个喂不同动物,动物吃东西的功能。
不用多态来实现,扩展性不好。
*/
Dog dog = new Dog();
Cat cat = new Cat();
Test2 t = new Test2();
t.feedDog(dog);
t.feedCat(cat);
}
public void feedDog(Dog dog){
dog.eat();
}
public void feedCat(Cat cat){
cat.eat();
}
}
public class Test3 {
public static void main(String[] args) {
/*
创建一个喂不同动物,动物吃东西的功能。
用多态来实现。
把子类类型上升为父类类型,可以用父类类型表示所有的子类对象 (多态的好处)
*/
Animal dog = new Dog();
Animal cat = new Cat();
Test3 t = new Test3();
t.feedAnimal(dog);
t.feedAnimal(cat);
}
public void feedAnimal(Animal animal){
animal.eat();
// animal.play(); animal中无play()方法因而报错,需要向下转型
// Dog dog = (Dog) animal;
// dog.play(); //Cat类中无play()因而cat报错
//向下类型转换时,需要做一个判断
//关键:instanceof 检测animal在运行时,实际的类型是否为Dog,如果是返回true,否则返回false。
if(animal instanceof Dog){
Dog dog = (Dog)animal;
dog.play();
}
}
}
抽象类
抽象就是一个概念,不是具体的。
抽象方法:
- 抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。
- 抽象方法必须用abstract关键字进行修饰。
- 在一些体系结构比较庞大的体系中,可以将顶层的类中的方法定义为抽象的。
抽象类:
-
如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
-
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法。
-
用abstract修饰的类就是抽象类。如果某个类中包含有抽象方法,那么该类就必须定义成抽象类
/* 如果一个类中有抽象的方法,name这个类必定是抽象类 但是抽象类中不一定有抽象方法 抽象类就是一个不完整的类,里面有抽象方法 抽象类因为没有具体实现的抽象方法,所以抽象类不能创建对象,除了不能创建对象外,其他功能和正常类一致。 */ public abstract class Animal { private String name; int age; /* abstravt 修饰的方法是抽象方法,可以没有具体的实现 只是作为一个功能的定义,在顶层类中往往只需要定义功能即可,让其他类去实现 */ public abstract void eat(); public Animal(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } /* 一个类继承了抽象类,可以继续声明成抽象类 */ public abstract class Dog extends Animal{ private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } } /* 哮天犬就是一个具体的类,重写父类中的抽象方法 */ public class AngelDog extends Animal{ @Override public void eat(){ System.out.println("哮天犬坐着用餐"); } }
public class Test { public static void main(String[] args) { AngelDog xtq = new AngelDog(); xtq.setAge(1000); //非抽闲的依旧可以调用 xtq.eat(); } }
抽象类特点:
- 抽象类不能被实例化,但可以有构造方法。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。
- 抽象类只能用作基类,表示的是一种继承关系。继承抽象类的非抽象类必须实现其中的所有抽象方法,而已实现方法的参数、返回值要和抽象类中的方法一样。否则,该类也必须声明为抽象类。
final关键字
final用于声明属性、方法和类。
-
属性:final修饰的属性是常量,值不可以改变,必须要初始化赋值。
//在定义时,直接为其赋值,值不可以改变。建议使用static赋值,在整个内存中值只有一份。 static final int num = 100; //在定义时,没有为常量赋值,必须在构造方法中赋值。在每一个对象中,都可以拥有一个常量。 final int count; //创建一个有参的构造方法 public finalDemo(int count) { this.count = count; } public void test(){ // num = 10; //final修饰无法赋值 new String("abc"); //只有创建对象时,在其构造方法中为其赋值。 new finalDemo(10); new finalDemo(20); }
-
方法:final修饰的方法不能被子类重写,不能修饰抽象的方法。
public final void test(){ //final修饰的方法不能被子类重写 }
-
类:修饰后的类不能被继承,final不能修饰抽象类、接口。
接口
接口(USB接口):专注于功能的设计,而不是实现。让其他类去具体实现即可。与抽象类类似,但有区别。
基本语法:
- 接口中所有属性默认为:public static final 静态常量
- 抽象方法默认:public abstract 修饰
- jdk8以后添加了静态方法,接口可以直接调用。
- jdk8以后添加了默认方法,通过子类调用。
public interface Myinterface {
// public static final int NUM = 10; //必须赋值 NUM常量 前面默认有public static final
int NUM = 10; //接口种定义的属性默认是静态常量
// public abstract void eat(); 前面默认是public abstract修饰
void eat();
void sleep();
//java8之后,新增加两种方法(静态方法,默认方法)
//静态方法 接口自己调用
public static void test1(){
System.out.println("test1 "+NUM);
}
//默认方法 是让子类重写,或者让子类调用
public default void test2(){
System.out.println("test2");
}
}
public class Test {
public static void main(String[] args) {
System.out.println(Myinterface.NUM);
Myinterface.test1(); //静态方法
// Myinterface.test2(); 默认方法无法直接调用
}
}
接口的继承:
- 一个类只能直接继承一个类
- 一个类可以实现多个接口
- 一个接口可以继承多个接口
public class MyinterfaceImpl extends Object implements Myinterface,InterfaceC{
//MyinterfaceImpl :Myinterface类的名字,Impl接口的实现
//静态方法不能重写,默认方法可重写可不重写
/*
一个类实现接口,要么将此类继续声明为抽象类;要么重写接口中的所有抽象方法。
*/
@Override
public void eat() {
}
@Override
public void sleep() {
}
//默认方法可重写可不重写
@Override
public void test2() {
}
}
public interface Myinterface extends InterfaceA,InterfaceB{}
接口的特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字
- 接口中方法可以是抽象的,静态的,默认的
- 接口中声明的属性默认为 public static final 的
- 接口不是被类继承了,而是要被类实现
- 接口不能实例化对象,无构造方法
- 一个类可以实现多个接口
- 与继承关系类似,接口与实现类之间存在多态性
- 一个接口能继承其它多个接口
- 当类实现接口的时候,类要实现接口中所有的抽象方法。否则,类必须声明为抽象的类
抽象类和接口的区别
相同点:
- 都不能创建对象
- 都可以表示多态性
不同点:
- 接口
- 接口不能有构造方法
- 不能有成员变量
- 不能有成员方法,只有静态方法、默认方法
- 抽象类
- 能有构造方法
- 能有成员变量
什么时候使用接口,什么时候使用抽象类:
public abstract class Animal {
private String name;
public Animal(){
}
public Animal(String name ){
this.name = name;
}
public abstract void eat();
public abstract void sleep();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Bird extends Animal implements CanFly,CanCry{
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void cry() {
}
@Override
public void fly() {
}
}
public class Dog extends Animal{
@Override
public void eat() {
}
@Override
public void sleep() {
}
}
public interface CanCry {
void cry();
}
public interface CanFly {
void fly();
}
public class Test {
public static void main(String[] args) {
Animal bird = new Bird();
CanFly b = new Bird(); //接口也可以作为父类类型来存在
CanCry c = new Bird();
// CanCry d = new Dog(); Dog没有CanCry的接口,无法实现
}
}