OOP面向对象
–1,面向对象是一种思想.相对面向过程而言的,相对简单.
–2,万物皆对象
–3,面向对象的特征:
–封装性,把相关的数据封装成一个“类”组件
–继承性,是子类自动共享父类属性和方法,这是类之间的一种关系
–多态,增强软件的灵活性和重用性
–4,类和对象的创建和使用
package cn.tedu.oop;
//测试 类和对象
//一个.java文件中,可以包含多个类.只不过,只能有一个类,被public修饰.
//而且,被public修饰的类名就是.java文件名.
public class Test6_Class {
public static void main(String[] args) {
//根据汽车类这个模板,创建一个汽车对象来干活
//使用new关键字,创建对象
// new Car();//匿名对象
Car BMW = new Car();//对象的名字叫BMW
//TODO 已经根据模板产生对象了,能不能使用模板里的功能????
}
}
//使用class关键字创建汽车类 ,用来描述汽车事物
class Car{
//描述事物的特征 -- 成员属性/实例变量/成员变量(在类里方法外)
//型号,颜色,价格,配置
double price ;
String color ;
String model ;
char peizhi ;
//描述事物的功能 -- 成员方法/成员函数
//前进 后退
public void run() {
System.out.println("run()...");
}
public void back() {
System.out.println("back()...");
}
}
类和对象的创建和使用
–1,代码
package cn.tedu.oop;
//测试 类和对象
//一个.java文件中,可以包含多个类.只不过,只能有一个类,被public修饰.
//而且,被public修饰的类名就是.java文件名.
public class Test6_Class {
public static void main(String[] args) {
//根据汽车类这个模板,创建一个汽车对象来干活
//使用new关键字,创建对象
// new Car();//匿名对象
Car c = new Car();//对象的名字叫BMW
//使用模板里的方法
c.run();
c.back();
//设置c对象的信息
c.price = 10.9 ;
c.peizhi = '高' ;
c.color = "black";
c.model = "X6";
//使用模板里的属性/变量
System.out.println( c.price );//0.0
System.out.println( c.peizhi );//\u0000
System.out.println( c.color );//null
System.out.println( c.model );//null
}
}
//使用class关键字创建汽车类 ,用来描述汽车事物
class Car{
//描述事物的特征 -- 成员属性/实例变量/成员变量(在类里方法外)
//型号,颜色,价格,配置
double price ;//小数类型的默认值是0.0
String color ;//引用类型的默认值是null
String model ;
char peizhi ;//char类型的默认值是\u0000
//描述事物的功能 -- 成员方法/成员函数
//前进 后退
public void run() {
System.out.println("run()...");
}
public void back() {
System.out.println("back()...");
}
}
–2,分析手机事物:
– 属性:颜色,尺寸,品牌,价格。。。
– 行为:打电话,发短信,听音乐。。。。
– 测试:
package cn.tedu.oop;
//练习手机类
public class Test1_Class {
public static void main(String[] args) {
//TODO 创建手机对象测试
//new Phone().call();//匿名对象--只干一件事情
Phone p = new Phone() ;
//调用模板里的方法
p.call();
p.music();
p.message();
//设置p对象的具体信息
p.color = "red" ;
p.size = 5.7 ;
p.pinpai = "HUAWEI" ;
p.price = 5999.9 ;
//调用模板里的属性
System.out.println( p.color );//null
System.out.println( p.size );//0.0
System.out.println( p.pinpai );//null
System.out.println( p.price );//0.0
}
}
//创建手机类 描述手机事物
class Phone{
//属性--成员变量--颜色,尺寸,品牌,价格
String color ;
double size ;
String pinpai ;
double price ;
//行为--成员方法--打电话,发短信,听音乐
public void call() {
System.out.println("call()..");
}
public void message() {
System.out.println("message()..");
}
public void music() {
System.out.println("music()..");
}
}
–3,创建多个对象测试
--代码
package cn.tedu.oop;
//练习手机类
public class Test1_Class {
public static void main(String[] args) {
//创建手机对象测试
new Phone().call();//匿名对象--只干一件事情
new Phone().music();//匿名对象--只干一件事情
Phone p = new Phone() ;
//调用模板里的方法
p.call();
p.music();
p.message();
//设置p对象的具体信息
p.color = "red" ;
p.size = 5.7 ;
p.pinpai = "HUAWEI" ;
p.price = 5999.9 ;
//调用模板里的属性
System.out.println( p.color );
System.out.println( p.size );
System.out.println( p.pinpai );
System.out.println( p.price );
//TODO 创建p2 p3对象测试
Phone p2 = new Phone() ;
//调用模板里的方法
p2.call();
p2.music();
p2.message();
//设置p对象的具体信息
p2.color = "white" ;
p2.size = 6.3 ;
p2.pinpai = "VIVO" ;
p2.price = 3999.9 ;
//调用模板里的属性
System.out.println( p2.color );
System.out.println( p2.size );
System.out.println( p2.pinpai );
System.out.println( p2.price );
Phone p3 = p2 ;
System.out.println( p3.color );
System.out.println( p3.size );
System.out.println( p3.pinpai );
System.out.println( p3.price );
}
}
//创建手机类 描述手机事物
class Phone{
//属性--成员变量--颜色,尺寸,品牌,价格
String color ;
double size ;
String pinpai ;
double price ;
//行为--成员方法--打电话,发短信,听音乐
public void call() {
System.out.println("call()..");
}
public void message() {
System.out.println("message()..");
}
public void music() {
System.out.println("music()..");
}
}
封装
–1,通过private关键字实现封装,来提高程序的安全性.可以封装成员变量,也可以封装成员方法.被private修饰后,只能在本类中可见.
–2,练习1:封装学生类
package cn.tedu.oop;
//测试封装
public class Test2_Private {
public static void main(String[] args) {
// 创建Student对象测试
Student s = new Student();
s.coding();
//s.chiji();//2,因为chiji()已经被封装了,外界无法使用!!
//设置属性的值
// s.name="jack";//4,因为name已经被封装了,外界无法使用!!
// s.age=20 ;
s.setName("jack");
s.setAge(20);
//获取属性的值
// System.out.println( s.name );//4,因为name已经被封装了,外界无法使用!!
// System.out.println( s.age );//0
System.out.println( s.getName() );//jack
System.out.println( s.getAge() );
// System.out.println( s.score );//0.0
}
}
// 提供Student类
class Student{
//特征
private String name ;
//5,当属性被private后,外界就没法直接获取或者设置了!!--提供公共的访问方式
//提供公共的设置方式 setXxx()
public void setName(String n){
//拿到你提供的n的值,给name属性赋值
name = n ;
}
//提供公共的获取方式 getXxx()
public String getName(){
return name;//把name属性的值,返回给外界的调用位置
}
//TODO 封装
private int age ;
public void setAge(int a) {
age = a;
}
public int getAge(){
return age ;
}
//自动-右键-source-generate getters and setters-select all-ok
private double score ;
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
//TODO 封装
private String addr ;
private long id ;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
//行为
public void coding() {
//3, 在类里, 提供间接的访问方式
chiji();
System.out.println("coding()...");
}
//1,通过private关键字,实现封装.只能在本类中访问
private void chiji() {
System.out.println("chiji()...");
}
}
构造方法
–1,用来创建对象 或者 完成对象的初始化.
–2,构造方法可以重载(方法名相同,参数列表不同)
–语法: 修饰符 类名([参数列表]){ 方法体 }
–3,构造方法创建对象
package cn.tedu.oop;
//测试 构造方法
//总结
//1, 构造方法的语法: 修饰符 类名(参数列表){}
//2, 构造方法的触发时间节点: 创建对象/new/实例化
//3, 构造方法可以重载,目的是为了,方便外界比较灵活的创建对象
public class Test3_Constructor {
public static void main(String[] args) {
//1,创建Teacher对象
Teacher t = new Teacher();//触发无参构造方法
//4,创建Teacher对象 -- 触发含参构造方法
Teacher t2 = new Teacher("张慎政");//触发String类型的含参构造
Teacher t3 = new Teacher(39);//触发int类型的含参构造
Teacher t4 = new Teacher("张慎政",39);//触发String和int的含参构造
}
}
//提供Teacher类
//构造方法语法: 修饰符 类名([参数列表]){ 方法体 }
class Teacher{
//2, 无参构造方法 默认就存在 ---前提是:不要只提供含参构造,否则就真没了!!
public Teacher() {
System.out.println(123);
}
//3, 含参构造方法 的重载 -- 方法名相同但是参数列表不同!!
public Teacher(String name) {
System.out.println("老师叫:"+name);
}
//重载的构造方法
public Teacher(int age) {
System.out.println("老师"+age+"岁.");
}
//TODO 重载的构造方法
public Teacher(String name,int age){
System.out.println("老师叫:"+name+",今年:"+age+"岁.");
}
}
–4, 构造方法赋值
package cn.tedu.oop;
//测试 构造方法
//总结
//1, 构造方法的语法: 修饰符 类名(参数列表){}
//2, 构造方法的触发时间节点: 创建对象/new/实例化
//3, 构造方法可以重载,目的是为了,方便外界比较灵活的创建对象
public class Test3_Constructor {
public static void main(String[] args) {
//1,创建Teacher对象
Teacher t = new Teacher();//触发无参构造方法
//4,创建Teacher对象 -- 触发含参构造方法
Teacher t2 = new Teacher("张慎政");//触发String类型的含参构造
Teacher t3 = new Teacher(39);//触发int类型的含参构造
Teacher t4 = new Teacher("张慎政",39);//触发String和int的含参构造
}
}
//提供Teacher类
//构造方法语法: 修饰符 类名([参数列表]){ 方法体 }
class Teacher{
String name ;
//2, 无参构造方法 默认就存在 ---前提是:不要只提供含参构造,否则就真没了!!
public Teacher() {
System.out.println(123);
}
//3, 含参构造方法 的重载 -- 方法名相同但是参数列表不同!!
public Teacher(String n) {
name = n ; // ----------!!!构造方法还可以给成员变量赋值!!!---------
System.out.println("老师叫:"+n);
}
//重载的构造方法
public Teacher(int age) {
System.out.println("老师"+age+"岁.");
}
//重载的构造方法
public Teacher(String name,int age){
System.out.println("老师叫:"+name+",今年:"+age+"岁.");
}
}
构造代码块和局部代码块
–1,代码块就是一段代码被一对花括号包起来的现象.如: {…}
–2,代码块随着位置的不同,作用和意义都不同.
–3,代码块如果在成员位置(在类里方法外),叫做构造代码块.
–4,代码块如果在局部位置(方法里),叫做局部代码块.
–5,构造代码块------用来提取构造方法的共性
–6,局部代码块------用来控制变量的作用范围
–7,测试
package cn.tedu.oop;
//测试 代码块
//总结
//1,构造代码块 -- 位置:类里方法外 -- 作用:抽取构造方法的共性
//2,局部代码块 -- 位置:方法里 -- 作用:控制变量的作用范围
//3,执行顺序: 构造代码块 > 局部代码块
//4,触发时机:
//当创建对象时,会触发构造方法,但是,如果有构造代码块,构造代码块>构造方法
//当方法被调用时,才触发局部代码块
public class Test1_Block {
public static void main(String[] args) {
//创建Person对象测试
new Person() ;
new Person("jack").show() ;
}
}
//创建Person类
class Person{
String country ;
//1, 构造代码块---位置是在类里方法外 -- 用来抽取构造方法的共性
//2, 在创建对象时,先触发构造代码块再触发构造方法
{
country = "中国人";//抽取构造方法的共性
System.out.println("构造代码块");
}
//构造方法
public Person() {
System.out.println("无参构造"+country);
}
public Person(String name) {
System.out.println("含参构造"+country);
}
public void show() {
//4,局部代码块--位置是在方法里--作用是控制变量的作用范围
{
int sum = 10 ;
System.out.println(sum);
System.out.println("局部代码块");
}
//System.out.println(sum);//已经超出了sum的作用范围
}
}
this关键字
–1,代表本类对象的引用对象
–2,当变量名相同时
package cn.tedu.oop;
//测试 this关键字
//总结
//当局部变量名和成员变量名相同时,会优先使用局部变量,
//想要使用成员变量必须通过this调用
public class Test2_This {
public static void main(String[] args) {
//TODO 创建Student对象测试
new Student().show();
}
}
//创建Student类
class Student{
int count ; //成员变量:位置在类里方法外,不用赋值也有默认值
int sum = 20 ;
//1,当成员变量 和 局部变量 同名时,通过this关键字调用成员变量!
//提供普通方法
public void show() {
int sum = 10 ;//局部变量:位置在方法里,必须赋值
System.out.println(sum);//就近原则,使用了局部变量10
//TODO 如何使用成员变量sum呢?--创建一个本类的对象
//2,this代表的是本来对象的引用,底层会自动创建本类对象Student this = new Student();
System.out.println( this.sum );//使用了成员变量20
System.out.println( count );//0
}
}
–3构造方法间的调用
package cn.tedu.oop;
//测试 this -- 可以在构造方法间 实现互相调用
public class Test3_This2 {
public static void main(String[] args) {
//创建对象测试
new ThisDemo() ;
// new ThisDemo(10) ;
}
}
//创建ThisDemo
class ThisDemo{
//提供构造方法
public ThisDemo() {
//2,在无参构造中 调用 含参构造
this(5);//3,this关键字出现在构造方法中时,必须是第一条语句!!
System.out.println("无参构造方法");
}
public ThisDemo(int a) {
//1,在含参构造中 调用 无参构造
//this() ;
System.out.println("含参构造方法"+a);
}
}
继承
–1,继承的初衷:提高代码的复用性.
–2,好处:
假设没有继承结构,每种小动物都需要提供吃的功能,可能得写100万次,这个业 务相同代码量非常大降低了开发效率.产生继承后,只需要把共性的吃的功能在父 类里写一次即可,哪个小动物需要提供吃,就来继承父类.
–3,特点
–使用extends关键字
–相当于子类把父类的功能复制了一份
–java只支持单继承
–继承可以传递(爷爷,儿子,孙子的关系)
–不能继承父类的私有成员
–继承多用于功能的修改,子类可以拥有父类的功能的同时,进行功能拓展
–像是is a 的关系
–4,入门案例
package cn.tedu.oop;
//测试 继承
public class Test4_Extends {
public static void main(String[] args) {
//TODO 创建Dog对象测试
Dog d = new Dog();
d.eat();//使用了父类的eat()
// System.out.println( d.sifangmoney );//5,父类中的private资源不能继承
System.out.println( d.country ); //7,继承具有传递性,使用了爷爷类的功能
}
}
class Yeye{
String country = "中国人";
}
//1,创建父类,抽取共性功能,来提高代码的复用性
class Animal extends Yeye{
//4,在父类中,如果不想让子类继承,可以用private修饰
private double sifangmoney = 100000;
public void eat() {
System.out.println("吃啥都行");
}
}
//2,使用extends关键字,表示继承关系--java只支持单继承
class Dog extends Animal{
//3,子类把父类的功能复制了一份
}
class Cat extends Animal{//6,像是is a的关系--强制的依赖关系
}
//.......
–5,super关键字
–代表的是父类对象的一个引用
–用来在子类中使用父类的功能
–如果用在构造方法中,super必须是第一条语句.
–6,方法的重写Override
– 发生在父类子间的现象
– 子类继承父类后,就可以使用父类的所有功能.
– 当子类想要修改父类原有的功能时,就会发生方法的重写/复写/覆盖
– 重写的要求 : 继承关系 + 在子类中方法的声明/签名 必须和 父类一模一样
– 方法的声明/签名 包括: 方法返回值 方法名(参数列表)
继承中的用法
–1,构造方法
package cn.tedu.oop;
//测试 继承中 的构造方法
//总结
//1, 子类的构造方法中,默认就会存在super() – 去找父类的无参构造
//2, 如果父类没有提供无参构造,只提供了含参构造–只能在子类构造方法中调用父类的含参构造
//3, 提示:每个类,最好都提供一个无参构造.
public class Test7_UseExtends3 {
public static void main(String[] args) {
//2,当你创建了子类对象时,实际上,会先触发父类的构造方法,然后是子类的构造方法
Erzi2 zi = new Erzi2();
}
}
class Baba2{
//4,无参构造 ,暂时不用也要提供,为了自己创建对象方便,或者为了子类.
public Baba2() {}
public Baba2(int a) {
System.out.println("父类的构造方法");
}
}
class Erzi2 extends Baba2{
public Erzi2() {
//1,子类的构造方法中,默认就会存在super()--会去找父类的无参构造
//super();
//3,如果父类中,没有无参构造呢? --只能调用父类的含参构造!!
//4,super关键字,如果在构造方法中出现,位置上必须是第一条语句!!类似于this
super(10) ;
System.out.println("子类的构造方法");
}
}
–2,成员变量
package cn.tedu.oop;
//测试 继承中 的成员变量
//总结
//1,在发生了继承关系后,子类中 想要用 父类的资源 ,最好都加上super
public class Test5_UseExtends {
public static void main(String[] args) {
Zi zi = new Zi();
System.out.println( zi.skin );//1,使用了父类的skin
zi.show(); //3,使用子类的show()
}
}
class Fu{
String skin = "yellow" ;
int num = 30 ;//4,当父类的变量 和 子类的变量 同名时
}
class Zi extends Fu{
int num = 20 ;//成员变量
//2,子类自己扩展的功能
public void show() {
int num = 10 ;//局部变量
System.out.println(num);//10,局部变量
System.out.println(this.num);//成员变量
System.out.println(super.skin);//使用父类的skin--不同名时直接用!!
//5,在子类中,如何使用父类的num --- 通过super关键字
//super代表父类对象的引用,相当于底层帮你创建了父类对象-- Fu super = new Fu();
//使用父类的num--同名了,用super调用父类资源
System.out.println( super.num );
}
}
–3,成员方法
package cn.tedu.oop;
//测试 继承中 的成员方法
//总结
//1,子类继承了父类后
//--可以使用父类的所有功能(除了private的)
//--还可以进行自己特有的功能扩展
//--继承后,如果某些功能的业务发生了改变,还可以重写
//--重写的要求是: 子类的 方法声明 必须 和父类 一模一样
public class Test6_UseExtends2 {
public static void main(String[] args) {
//创建子类对象测试
Erzi zi = new Erzi();
//4,在重写前,使用的是父类的吃的功能.重写后,使用了子类的功能
zi.eat();
zi.coding();//子类可以使用自己特有的扩展功能
}
}
class Baba{
public void eat() {
System.out.println("爸爸在吃肉");
}
}
class Erzi extends Baba{
//1,子类可以使用父类的方法--发生了继承关系
//2,子类可以进行功能拓展
public void coding() {
System.out.println("学java!");
}
//3,子类想要修改父类的原有功能 -- 方法重写/覆盖/复写
//要求:子类的方法声明 必须和 父类一模一样
public void eat() {
//5,虽然发生了方法重写,但是,对于父类原有功能没有任何影响!!!
//super.eat();//调用父类的功能,查看一下
System.out.println("娃在喝汤");
}
}
程序设计
– 假设现在有一个汽车类,我们可以根据汽车类创建很多汽车对象。
– 需求:
1,创建汽车类。提供启动、停止、运行功能
2,创建子类,继承汽车类。覆盖/重写 启动和停止功能
3,创建子类对象,进行子类的功能测试
– 代码 :
package cn.tedu.oop;
public class Test8 {
public static void main(String[] args) {
Audi audi = new Audi();
audi.start();//使用了父类的
audi.stop();//使用了父类的
audi.run();//使用了父类的
BMW bmw = new BMW();
bmw.start();//重写了,使用了子类的
bmw.stop();//重写了,使用了子类的
bmw.run();//没重写,使用了父类的
}
}
//创建父类
class Car{
//提供启动、停止、运行功能
public void start() {
System.out.println("已启动!");
}
public void stop() {
System.out.println("已停止!");
}
public void run() {
System.out.println("冲鸭!");
}
}
//创建子类
class BMW extends Car{
//BMW继承后,想改启动和停止
public void start() {
System.out.println("BMW已启动!");
}
public void stop() {
System.out.println("BMW已停止!");
}
}
//Audi直接继承,没有修改功能的需求
class Audi extends Car{
}
static关键字
–1,特点
1、 可以修饰成员变量,成员方法
2、 随着类的加载而加载,优先于对象加载
3、 只加载一次,就会一直存在,不再开辟新空间
4、 全局唯一,全局共享
5、 可以直接被类名调用
6、 静态只能调用静态,非静态可以随意调用
7、 static不能和this或者super共用,因为有static时可能还没有对象
–2,入门案例
package cn.tedu.staticdemo;
//测试 static
public class Test1_Static {
public static void main(String[] args) {
//3,如何调用静态资源呢? -- 静态资源多了一种调用方式,可以类名直接调用
//4,随着类的加载而加载,优先于对象加载
Person.test();
System.out.println( Person.age );
//TODO 创建对象测试
Person p = new Person();
p.show();
System.out.println( p.name );
//2,如何调用静态资源呢? -- 通过对象访问
p.test();
System.out.println( p.age );
//5,静态资源是共享资源,在多个对象间数据共享
Person p2 = new Person();
Person p3 = new Person();
p2.age = 10 ;
System.out.println( p3.age );//10,age在p2和p3间共享
}
}
class Person{
//静态资源
//1,static 可以用来修饰成员变量和成员方法
static int age;
static public void test() {
//this.show();--Cannot use this in a static context
//super. --Cannot use this in a static context
//7, 静态资源 可以调用 普通资源吗? -- 不可以-静态只能调用静态
// show();
// System.out.println(name);
System.out.println("test()...");
}
//普通资源
String name ;
public void show() {
//6,普通资源 可以调用 静态资源吗? -- 可以
test();
System.out.println(age);
System.out.println("show()...");
}
}
静态代码块、构造代码块、局部代码块
–1,静态代码块通常用于完成项目的初始化.
–2,静态资源会随着类的加载而加载,第一时间加载进内存,并一直贮存在内存中,直到类消失静态资源才消失.
–3,静态资源只会被加载一次
–4,静态代码块,存在的位置是成员位置.如:static{…}
–5,测试
package cn.tedu.staticdemo;
//测试 代码块
//总结
//静态代码块 --位置:在成员位置--作用:完成项目初始化--触发节点:类加载时
//构造代码块 --位置:在成员位置--作用:抽取构造方法的共性--触发节点:创建对象时
//局部代码块 --位置:在局部位置--作用:控制变量的作用范围--触发节点:方法执行时
//执行顺序:静态代码块 > 构造代码块 > 局部代码块
public class Test2_Block {
public static void main(String[] args) {
new BlockDemo();//触发了构造代码块和构造方法
new BlockDemo();
new BlockDemo().test();//触发了构造代码块和构造方法和局部代码块
}
}
class BlockDemo{
//静态代码块
static{
System.out.println("静态代码块");
}
//构造代码块
{
System.out.println("构造代码块");
}
//构造方法
public BlockDemo() {
System.out.println("构造方法");
}
//局部代码块
public void test() {
{
System.out.println("局部代码块");
}
}
}
final关键字
–1,在继承结构中,父类的功能可以全都给子类用. 但是当子类想要修改功能 时,会发生方法的重写现象.子类想改哪个方法都可以重写.如果想要限制, 不让子类随便重写.可以把父类的功能用final修饰成最终的.
–2,测试
package cn.tedu.oop;
//测试 final关键字
public class Test3_Final {
public static void main(String[] args) {
Zi zi = new Zi();
zi.eat();//重写前使用了父类的功能,重写后使用子类的功能
//zi.age = 20;//修改了age的值
System.out.println(zi.age);//使用了age的值
}
}
//1,final修饰类不能被继承--The type Zi cannot subclass the final class Fu
//final class Fu{
class Fu{
//2,final修饰方法可以被继承,但是不能被重写--Cannot override the final method from Fu
final public void eat() {
System.out.println("Fu...eat()...");
}
//3,final修饰的是常量,值不能被修改--The final field Fu.age cannot be assigned
final int age = 1_000 ;
}
class Zi extends Fu{
/*
* //需要修改父类原有的功能--方法重写--方法声明必须和父类一模一样
*
* @Override public void eat() { System.out.println("Zi...eat()"); }
*/
}
多态
–1,多态是指同一个对象有多种形态.
–2,多态是为了统一调用标准—父类就是标准,一切向父类看齐
–3,好处是:不关心具体的类型,屏蔽了子类之间的不同,把子类都会当做父类来看,做出统一的编程,写 出通用的代码.
–4,特点:
–前提: 发生继承关系 + 发生方法重写
–口诀: 父类引用指向子类对象 + 编译看左边运行看右边
–5,入门案例
package cn.tedu.oop;
//测试 多态
public class Test4_Multi {
public static void main(String[] args) {
//TODO 创建子类对象测试
Dog d = new Dog();
d.eat();//重写了,用了子类的
//TODO 创建多态对象测试
//口诀1:父类引用 指向 子类对象-相当于把子类转成父类
Animal a = new Dog();
//口诀2:编译看左边,运行看右边
a.eat();//使用了父类的方法声明,使用了子类的方法体
//编译看左边 -- 是指 想要成功的保存,就要使用左边也就是 只能使用父类提供的功能!!
//运行看右边 -- 是指 想要得到结果,就要看右边也就是 使用子类的方法体!!!
}
}
class Animal{
public void eat() {
System.out.println("吃奥特曼");
}
}
//1,多态的前提1:发生继承关系
class Dog extends Animal{
//2,多态的前提3:发生方法重写
@Override
public void eat() {
System.out.println("狗吃屎");
}
}
多态的好处
–1,提高了程序的灵活性和扩展性
–2,多态中,根本不关心具体的子类的类型,可以屏蔽子类间的不同,把子类都当做父类对待
–3,代码更加通用,做出统一的编程
–4,代码体现:
class Test{
//现在的eat()具体的指定了要吃啥!!!太死板.
//怎么灵活?如果能够不关心具体是哪种小动物.
eat(Aniaml a){ }//多态,只要是小动物都能吃
eat(Dog a){ }//写死了,必须是小狗吃
eat(Cat a){ }//写死了,必须是小猫吃
eat(Tiger a){ }//写死了
eat(… a){ }//写死了
eat(… a){ }//写死了
}
多态的使用
–1,代码
package cn.tedu.oop;
//测试 使用多态
//成员变量.成员方法.静态方法
public class Test5_UseMulti {
public static void main(String[] args) {
//创建多态对象测试 -- //口诀1:父类引用 指向 子类对象
Father f = new Son();
//口诀2: 编译看左边(只能用父类提供的资源),运行看右边(结果只能以子类为准-针对重写方法)
//1,多态对象使用的成员变量 --- 使用父类的10
System.out.println( f.sum );
//2,多态对象使用的成员方法 --- 使用了父类的方法声明,使用了子类的方法体
f.study();
//3,静态方法可以重写吗 --- 不可以!!
f.show();//使用了父类的方法声明 和 方法体
Father.show();//静态资源,谁调用就执行谁的功能,根本没有重写的说法!!
}
}
class Father{
int sum = 10 ;
public void study() {
System.out.println("学习养生");
}
static public void show() {
System.out.println("Father...show()");
}
}
class Son extends Father{
int sum = 20 ;
@Override
public void study() {
System.out.println("学习敲代码");
}
static public void show() {
System.out.println("Son...show()");
}
}
异常
–1,异常是指程序中出现的Bug.
–2,继承结构非常严谨,Throwable-Exception-…
–3,Exception是程序中所有异常的父类
–4,异常处理
–捕获:把程序中会出现的异常自己处理掉
try{
代码
}catch(异常类型1 异常名){
给出解决方案1
}catch(异常类型2 异常名){
给出解决方案2
}
–抛出:把程序中出现的异常自己不管,交给调用者去管
–5,测试
package cn.tedu.oop;
import java.util.InputMismatchException;
import java.util.Scanner;
//测试 异常
public class Test6_Exception {
public static void main(String[] args) {
// method() ; //暴露异常
method2() ; //异常处理!!!--捕获异常
}
//捕获异常
private static void method2() {
try {//尝试执行try里的代码
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}catch(ArithmeticException a) {//如果分母为0了立刻捕获
System.out.println("两次输入不能为0!");
}catch(InputMismatchException a) {//如果类型不对了立刻捕获
System.out.println("请输入两次整数!");
}catch(Exception a) {//如果有其他异常立刻捕获
//多态的好处:不关心具体的子类类型,把子类当父类来看。写出通用代码
System.out.println("请输入正确的数据!");
}
//现在的问题是:程序中也许不仅仅只有两种异常,还可能有更多异常
//但是,我根本不知道还有几个异常,异常的名字叫什么????
}
//暴露异常
public static void method() {
//接收键盘输入的两个整数
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
//做除法运算
System.out.println(a/b);
}
}
异常处理
–1,抛出:自己不处理,谁调用谁处理
–在方法声明上添加代码 ,如: throws 异常类型
–2,测试
package cn.tedu.oop;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Test1_Exception {
// public static void main(String[] args) throws Exception{
public static void main(String[] args){
// method();//暴露异常
try {
method2();
}catch(Exception e){
//3,method2抛出异常自己没管,调用者来处理抛出的异常Exception
System.out.println("运算异常!!");
}
}
//1,抛出异常--在方法声明上添加throws 异常类型1,异常类型2
public static void method2()
// throws ArithmeticException,InputMismatchException,?, ?{
throws Exception{
//2,多态,根本就不关心具体的异常类型,都会把子类当父类来看
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}
//暴露异常
public static void method() {
//接收键盘输入的两个整数
int a = new Scanner(System.in).nextInt();
int b = new Scanner(System.in).nextInt();
//做除法运算
System.out.println(a/b);
}
}
抽象类
–1,子类继承了父类,就可以使用父类的eat().但是吃的不一样,也可以进行重写.改的是方法体…
–2,是一个特殊的方法,特殊在这个方法根本就没有方法体–抽象方法
–3,如果类里有抽象方法,那么这个类就是一个抽象类
–4,抽象类里面的方法 可以没有方法体
–5,入门案例
package cn.tedu.oop;
//测试 抽象类
public class Test2_Abstract {
public static void main(String[] args) {
//7,抽象类多用于多态中
Animal a = new Cat(); //口诀1:父类引用指向子类对象
a.eat();//口诀2:编译看左边--功能向父类看齐,父类就是标准
a.sleep();//运行看右边--针对方法重写--结果要看子类怎么干的
//6, 抽象类不能被实例化
// Animal b = new Dog();
}
}
//普通类,普通方法
//3, 如果类里包含抽象方法 ,那么这个类就是一个抽象类
abstract class Animal{
//0,分析后,发现,如果子类继承父类后,吃的都不一样,肯定会发生修改发生方法重写的现象
//方法体肯定会被修改,父类干脆也就不提供方法体了--变成了抽象方法
//1,通过abstract关键字来描述抽象
//2,抽象方法 只有方法声明 没有方法体
abstract public void eat() ;
//0,方法体不会被改,父类提供还是有意义的
public void sleep() {
System.out.println("呼呼大睡");
}
}
//4,子类继承 抽象类以后,可以有两个选择:要么是一个抽象类
abstract class Dog extends Animal{
}
//5,子类继承 抽象类以后,可以有两个选择:要么把所有抽象方法都重写
class Cat extends Animal{
//abstract public void eat() ;
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
抽象类的用法
–1,构造函数
package cn.tedu.oop;
//测试 抽象类使用构造方法
//总结
//3,抽象类里是可以有 构造方法的--干嘛用--不是为了让自己new,而是为了让子类new
public class Test3_UseAbstract {
public static void main(String[] args) {
//一定会触发子类的构造方法
new Dog2();
// new Animal2() ;
}
}
//抽象类
abstract class Animal2{
//3,抽象类里是可以有 构造方法的--干嘛用--不是为了让自己new,而是为了让子类new
public Animal2() {
System.out.println("父类的 构造方法");
}
}
class Dog2 extends Animal2{
//1,子类有没有构造方法? -- 有一个默认的无参构造
public Dog2() {
//2,隐藏着super()--会去找父类的 无参构造
super();
System.out.println("子类的 构造方法");
}
}
–2,抽象类的成员变量
package cn.tedu.oop;
//测试 抽象类使用成员变量
//总结
//抽象类里可以有变量也可以有常量
public class Test4_UseAbstract2 {
public static void main(String[] args) {
Animal3 a = new Dog3();
a.name = "二哈" ;//变量可以修改值
System.out.println(a.name);//变量可以获取值
// a.MAX_VLAUE = 126 ;//常量的值不可以被修改
System.out.println(a.MAX_VLAUE);//常量可以直接获取值
}
}
abstract class Animal3{
//1,抽象类中 可以提供 变量
String name = "大黄";
//2,抽象类中 可以提供 常量
public static final byte MAX_VLAUE = 127 ;
}
class Dog3 extends Animal3{
}
–3, 抽象类的成员方法
package cn.tedu.oop;
//测试 抽象类使用成员方法
public class Test5_UseAbstract3 {
public static void main(String[] args) {
//创建对象测试
Animal4 a = new Dog4();
a.sleep();//普通方法--重写前用父类的,重写后用子类的
a.eat();//抽象方法--用了父类的方法声明,子类的方法体
a.game();//抽象方法--用了父类的方法声明,子类的方法体
}
}
//1,如果类里都是普通方法,这个类仍然被修饰成了抽象类,为什么?--抽象类不能实例化
//2,抽象类是一个特殊的类,非常灵活.特殊在类里可以有抽象方法也可以有普通方法.
abstract class Animal4{
//普通方法--提供方法体
/*
* public void eat() { System.out.println("eat()..."); }
* public void game() { System.out.println("game()..."); }
*/
public void sleep() {
System.out.println("sleep()...");
}
//抽象方法--没有方法体
abstract public void eat() ;
abstract public void game() ;
}
//3,抽象类的子类,需要把所有抽象方法都重写,否则就是一个抽象类
//而继承来的普通方法看需求,需要修改就重写不改也可以不重写
class Dog4 extends Animal4{
@Override
public void eat() {
System.out.println("eat()...");
}
@Override
public void game() {
System.out.println("game()...");
}
}
//4,抽象类的子类,可以还是一个抽象类(因为包含着继承来的抽象方法)
abstract class Cat4 extends Animal4{
//Animal4里面有两个抽象方法,你只重写一部分的话,就说明还包含着没重写的抽象方法
//还得是一个抽象类
// abstract public void eat() ;
// abstract public void game() ;
@Override
public void eat() {
System.out.println("Cat4..eat()");
}
}
接口
–1,概念
–1,接口来的目的就是为了突破java单继承的局限性
–2,接口的好处:灵活,可以同时使用多个接口的功能
–3,接口主要体现的是 一套开发规范
–2,特点
1、 接口中都是抽象方法
2、 通过interface关键字创建接口
3、 通过implements让子类来实现
4、 可以理解成,接口是一个特殊的抽象类
5、 接口突破了java的单继承的局限性
6、 接口和类之间可以多实现,接口和接口之间可以多继承
7、 接口是对外暴露的规则,是一套开发规范
8、 接口提高了程序的功能扩展,降低了耦合性
–3,入门案例
package cn.tedu.oop;
//测试 接口
public class Test1_Interface {
public static void main(String[] args) {
//TODO 创建多态对象测试
//9,接口可以被实例化吗?--不可以,接口和抽象类一样都不能被实例化
// new Demo() ;
//8,用了接口里的抽象方法的声明,实现类的方法体
Demo demo = new DemoImpl();
demo.eat();
demo.game();
demo.hi();
}
}
//1,抽象类里可以有抽象方法(必须被abstract修饰),也可以有普通方法(提供方法体)
//3,通过interface关键字定义接口,语法:interface 接口名
interface Demo{
//2,接口里的方法都是抽象方法
abstract public void eat() ;
abstract public void game() ;
//4,在jdk1.8里允许接口中出现普通方法,要求被static或者default修饰
// static public void hi() {}
// default public void hi() {}
//5,接口为方法的编写提供了简写方式,会自动拼接 public abstract
// public abstract void hi() ;
void hi() ;//简写形式
}
//6,如果想用接口里的功能,怎么用呢?--和接口发生实现关系
//class Dog extends Animal{//继承时,是子类继承抽象类,来使用抽象类的功能
class DemoImpl implements Demo{//现在是,实现类 实现 接口,来使用接口的功能
//7,实现接口后,由于接口里都是抽象方法,所以需要全都重写,否则就是一个抽象类
@Override
public void hi() {
System.out.println("hi()...");
}
@Override
public void eat() {
System.out.println("eat()...");
}
@Override
public void game() {
System.out.println("game()...");
}
}
//abstract class DemoImpl implements Demo{//现在是,实现类 实现 接口,来使用接口的功能
接口的用法
–1,构造方法
–2,成员变量
–3,成员方法
–4,测试
package cn.tedu.oop;
//测试 接口
//总结
//接口里没有构造方法/没有变量都是常量
//接口里都是抽象方法(jdk1.8可以有特殊的普通方法)
//接口里的常量可以简写,会自动拼接public static final
//接口里的方法可以简写,会自动拼接public abstract
public class Test2_Interface2 {
public static void main(String[] args) {
//3,接口不能被实例化
Inter in = new InterImpl();
//in.age = 20; //修改值,失败---age是final的
System.out.println( in.age );//获取值
System.out.println( Inter.age );//获取值,age是static的
//TODO 7,测试接口里的方法
String desc = in.save();
System.out.println(desc);
in.delete(10);
}
}
//0,通过interface定义接口
interface Inter{
//1,接口里不让出现构造方法.Interfaces cannot have constructors
// public Inter() { }
//2,接口里没有变量!!! -- 会自动拼接public static final把变量变成常量!!
//public static final int age = 10;
int age = 10 ; //简写形式
//4,接口里的方法--会自动拼接public abstract
// public abstract void save() ;
// public abstract void delete() ;
String save() ;//简写形式
void delete(int id) ;
}
//5,接口的实现类,要么重写所有抽象方法,要么是一个抽象类
//abstract class InterImpl implements Inter{
class InterImpl implements Inter{
//6,在进行方法重写时,要有足够的权限.接口里的所有资源默认的权限都是public
@Override
public String save() {
return "保存成功!" ;
}
@Override
public void delete(int id) {
System.out.println("delete()..."+id);
}
}
接口的复杂用法
–接口和类之间可以多实现,接口和接口之间可以多继承
–测试
package cn.tedu.oop;
import java.io.Reader;
//测试 接口的复杂用法
//接口和类之间可以多实现,接口和接口之间可以多继承
public class Test3_Interface3 {
public static void main(String[] args) {
//4,--2号接口的功能,接口那么多,想用谁的功能,左边就写谁
Inter2 in = new Inter2Impl() ;
in.update();
in.save();
in.delete(5);
in.get();
}
}
interface Inter1{
void save();
}
interface Inter3{
void update();
void get();
}
//5,实现类可以在继承的同时,多实现
abstract class Impl2 extends Object implements Inter1 , Inter3{
}
//3,接口和实现类之间 -- 是实现关系 ,可以多实现(逗号隔开)
//--Impl实现类,同时实现了Inter1和Inter3接口的功能,
//--需要同时重写多个抽象方法,否则就是一个抽象类
class Impl implements Inter1 , Inter3{
@Override
public void update() {
}
@Override
public void get() {
}
@Override
public void save() {
}
}
//1,接口和接口之间 -- 是继承关系,可以多继承(逗号隔开)
//--2号接口同时使用了1号和3号接口的功能!
interface Inter2 extends Inter1 , Inter3{
void delete(int id) ;
}
//2,想要使用2号接口的功能,找一个实现类来实现接口
class Inter2Impl implements Inter2{
@Override
public void save() {
System.out.println(1);
}
@Override
public void update() {
System.out.println(2);
}
@Override
public void get() {
System.out.println(3);
}
@Override
public void delete(int id) {
System.out.println(4);
}
}
接口和抽象类的区别
–1,类和类间的关系:是继承关系
–java里只支持单根继承
–class A extends B
–其中A是子类,B是父类.子类可以使用父类的所有功能
–方法的重写override:如果想要修改父类的原有功能,可以进行方法重写
–2,接口和接口间的关系:是继承关系
–接口的出现就是为了突破java单继承的局限性
–接口间可以多继承
–interface A extends B , C
–其中A是子接口,B和C是父接口
–A就拥有了B和C的所有功能,A的功能是最全的
–class Impl implements A
–Impl实现类就需要同时重写A B C 接口里的所有抽象方法,否则就是一个抽象类
–3,类和接口间的关系:是实现关系
–实现类可以实现接口,而且可以多实现
–class A implements B,C
–A是实现类,B和C是接口.
–A需要同时重写B和C接口里的所有抽象方法,否则就是一个抽象类
–类可以在继承的同时多实现
–class A extends B implements C , D
–A可以叫子类,也可以叫是实现类
–A同时拥有父类B的功能,也同时拥有接口C和D的功能
–A需要同时重写C和D接口里的所有抽象方法,否则就是一个抽象类
–对于父类B里的方法吗?看需求,B类是父类,里面如果都是普通方法,只有需要修改才会发生重写.
–4,抽象类和接口的区别
–相同点
–抽象类和接口都是抽象层,一般用来提取共性
–都不能被实例化
–不同点
–构造方法:抽象类里有,接口里没有!!
–成员变量:抽象类里有,接口里没有,接口里都是常量!!
–成员方法:抽象类类可以有普通方法和抽象方法,但是接口里都是抽象方法(1.8后可以有)
–接口里存在简写形式,抽象类里没有.
–接口里的常量,int age = 10;会为常量自动拼接public static final
–接口里的方法,void save();会为方法自动拼接public abstract
–怎么去设计你的抽象层,到底是体现为一个抽象类呢还是接口呢?
–关键就看你需不需要提供方法体,如果类里的方法都不需要提供方法体,可以设计为接口.如果类里的方法需要提供方法,设计为抽象类.
–抽象层到底设计成抽象类还是接口,谁好呢?
–如果实现类想要同时拥有多个功能,最好选择接口,因为接口可以多继承多实现
–如果就是设计为抽象类,子类就只能继承一个父类,只有这么一次的继承权
Java中有23 种设计模式,本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。
当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择。
1、 对于简单的程序,可能写一个简单的算法要比引入某种设计模式更加容易。
2、 但是对于大型项目开发或者框架设计,用设计模式来组织代码显然更好。
单例设计模式概念
单例模式可以说是大多数开发人员在实际中使用最多的,常见的Spring默认创建的bean就是单例模式的。
单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
RunTime就是典型的单例设计,我们通过对RunTime类的分析,一窥究竟。
源码剖析
/**
* Every Java application has a single instance of class
* Runtime
that allows the application to interface with
* the environment in which the application is running. The current
* runtime can be obtained from the getRuntime
method.
*
* An application cannot create its own instance of this class.
*
* @author unascribed
* @see java.lang.Runtime#getRuntime()
* @since JDK1.0
*/
RunTime.java
package java.lang;
public class Runtime {
//1、创建静态的全局唯一的对象
private static Runtime currentRuntime = new Runtime();
//2、私有构造方法,
/** Don't let anyone else instantiate this class */
private Runtime() {}
//3、通过自定义的静态方法获取实例
public static Runtime getRuntime() {
return currentRuntime;
}
}
饿汉式
目的:控制外界创建对象的个数只能创建1个对象
开发步骤:
1、 私有化构造方法
2、 在类的内部创建好对象
3、 对外界提供一个公共的get(),返回一个已经准备好的对象
package cn.tedu.single;
//测试单例设计模式
public class Test8_Single {
public static void main(String[] args) {
Single s = Single.get();
Single s1 = Single.get();
//get()多少次,内存中使用的都是同一个对象
System.out.println(s);//cn.tedu.single.Single@15db9742
System.out.println(s1);//cn.tedu.single.Single@15db9742
}
}
class Single{
// 1、私有化构造方法,不让外界直接new
private Single() {}
// 2、在类的内部,创建好对象
//static :静态只能调用静态
static private Single s = new Single();
// 3、对外界提供一个公共的get(),返回一个已经准备好的对象
//static是为了外界不通过对象访问而是通过类名直接方法
static public Single get(){
//注意:静态只能调用静态
return s;
}
}
懒汉式
class Single{
// 1、私有化构造方法,不让外界直接new
private Single() {}
// 2、在类的内部,创建好对象
//static :静态只能调用静态
static private Single s = null;
// 3、对外界提供一个公共的get(),返回一个已经准备好的对象
//static是为了外界不通过对象访问而是通过类名直接方法
synchronized static public Single get(){
//注意:静态只能调用静态
if(s==null){
s = new Single();//会有安全问题
}
return s;
}
}
abstract注意事项
抽象方法要求子类继承后必须重写。那么,abstract关键字不可以和哪些关键字一起使用呢?以下关键字,在抽象类中。用是可以用的,只是没有意义了。
1、 private:被私有化后,子类无法重写,与abstract相违背。
2、 static:静态的,优先于对象存在。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。
3、 final:被final修饰后,无法重写,与abstract相违背。
接口和抽象类的区别
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法,如果要扩展抽象类的新方法,子类将很容易的就能得到这些新方法。
8、如果一个类里有抽象方法,那么这个类只能是抽象类
9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
了解软件设计的开闭原则OCP
开放功能扩展,关闭源码修改。等
开闭原则的英文全称是Open Close Principle,缩写是OCP,它是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的、灵活的系统。
开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。
开闭原则,是一种设计模式,随着面向对象程序设计的思想,应运而生。
开:指的是可以在源代码的基础上进行扩展,比如继承,接口,抽象类等。在JAVA中,之所以用继承,是在可以直接调用类库的前提下,对其功能进行扩展。不需要应用者去了解封装类的内部逻辑就可以做开发。
闭:指不允许对原有的代码进行修改。以免影响其他现有功能,造成功能瘫痪。