注:本章节主要复习类和对象、Java语言的三大特性、构造方法、重载和重写、代码块相关知识。
类也是一种对象,用class来定义,类名遵循驼峰命名法。类是抽象的,是一类事物抽取共同属性与功能形成的
注意:类在现实世界中并不存在,它只是一种对象的数据类型。
对象就是根据类创建出来的一个个具体实例,一个类可以创建出多个对象。 通过每个对象自己唯一的地址值来区分不同的对象,每个对象都有自己的属性,而且属性互不影响。
对象具有各种功能,每个对象可以执行自己所拥有的功能。
/**本类用作面向对象的入门案例 * 设计手机这一类事物 * 分析属性:品牌、价格、尺寸、颜色 * 分析功能:打电话 发短信 看直播 * */ /**在一个java文件中可以写多个class,但是被public修饰的class只能有一个 * 而且要求这个公共类的名字必须要与文件名保持一致*/ public class TestCreateClass { public static void main(String[] args) { //创建对象 Phone p1 = new Phone(); //通过手机类的对象p1 p1.call(); p1.message(); p1.video(); System.out.println(p1.brand); System.out.println(p1.color); System.out.println(p1.price); System.out.println(p1.size); Phone p2 = new Phone(); p2.video(); p2.message(); p2.call(); p2.brand = "HUAWEI"; p2.color = "中国红"; p2.size = 5.6; p2.price = 6666.66; //查看对象 System.out.println(p2.brand); System.out.println(p2.color); System.out.println(p2.price); System.out.println(p2.size); System.out.println(p1);//cn.tedu.oop.Phone@1b6d3586 System.out.println(p2);//cn.tedu.oop.Phone@4554617c } } //通过class关键字创建手机类---用来描述手机这一类事物--属性+功能 class Phone{ //定义手机类的属性---用成员变量来描述--位置:类里方法外 String brand;//品牌 double price;//价格 double size;//尺寸 String color;//尺寸 //定义手机类的功能,用方法来描述,格式:修饰符 返回值类型 方法名(参数列表){方法体} public void call(){ System.out.println("在打电话"); } public void message(){ System.out.println("在发短信"); } public void video(){ System.out.println("在开直播"); } }
Phone p = new Phone();
(1).在栈内存中开辟一快空间,存放引用类型Phone类型的变量p;
(2).在堆内存中开辟一块空间,存放Phone类型的对象;
(3.).他要给对象进行初始化;比如:String brand = null;
(4).当对象准备好以后,会生成一个唯一的地址值,然后将次地址值交给引用类型的变量p来保存;
(5).如果以后想操作此对象的各种资源,可以通过p变量保存地址找到该对象,比如:p.call(); p.price=66;
在Java中,我们不需要手动管理对象和销毁对象,目前是有Java虚拟机进行管理和销毁,但是需要了解对象作用域。
对象作用域决定了其内部定义的变量名的可见性和生命周期,作用域通常由{}的位置来决定。
类的基本要素就是要具备属性和方法。属性也被称为字段,是类的重要组成部分,属性也可以是任意类型的对象,也可以是基本数据类型。
方法就是表示功能,具体实现,其实就是函数,方法的基本组成:方法名、参数、返回值和方法体。
与本类类名同名的方法,且没有返回值类型。
注意:
1).每次new对象时,都会触发执行对应类的构造方法;
2).每个类中默认都会存在一个没有参数的构造方法;但是,如果你提供了其他的构造函数,默认的构造函数会被覆盖,所以我们要手动提供无参构造,因为只有这样,才能不传参数也能创建对象;
3).构造方法格式:与本类类名同名,且没有返回值类型的方法;
构造方法分为:
无参构造:没有参数的构造方法;----默认存在,如果存在其他的构造,此构造会被覆盖。
含参构造:含有参数的构造方法;
全参构造:与本类的属性完全一致;----创建对象时,并且能给此对象的所有值赋值.
/**本类用作构造函数的巩固练习*/ public class TestCat { public static void main(String[] args) { Cat c1 = new Cat(); Cat c2 = new Cat("豆豆"); Cat c3 = new Cat("豆豆",5,110); System.out.println(c1.name); System.out.println(c2.age); System.out.println(c3.name); } } class Cat{ String name;//名字 int age;//年龄 double price;//价格 public void play(){ System.out.println("就是玩~"); } public Cat(){ System.out.println("这是无参构造"); } public Cat(String s){ System.out.println("这是含参构造:"+s); } public Cat(String name, int age, double price) { this.name = name; this.age = age; this.price = price; System.out.println("这是全参构造:"); } }
(1)重载@Overload
1).我们根据方法名+参数列表确定具体要调用的方法
2).方法的重载:
在通过一个类中,存在多个方法名相同,但参数列表不同的方法;如果在同类中,多个同名方法的参数个数不同,一定构成重载;如果在同类中,多个同名方法的参数个数相同:需要查看对应位置上的参数类型,而不是参数名,与参数名无关
* (int a ,String b)与(int b ,Sting a)---不构成
* (int a, String b)与(String a ,int b)---构成
同名同类不同参
/**本类用于构造方法与方法的重载快速回顾*/ public class TestDog { public static void main(String[] args) { Dog d1 = new Dog(); Dog d2 = new Dog("旺财"); Dog d3 = new Dog("旺财",52,3000); d1.play(); d1.play("二郎神"); d1.play(3,"骨头"); d1.play("哮天犬",7000); d2.play(); d2.play("二郎神"); d2.play(4,"骨头"); d2.play("哮天犬",8000); } } class Dog{ String name;//名字 int age;//年龄 double price;//价格 public void play(){ System.out.println("小狗喜欢玩飞盘~"); } public void play(String host){ System.out.println("小狗喜欢和主人"+host+"玩飞盘"); } public void play(int n, String food ){ System.out.println("小狗今晚要吃"+n+"份"+food); } public void play(String name,int age){ System.out.println("小狗"+name+"今年"+age+"岁了"); } public Dog(){ System.out.println("这是无参"); } public Dog(String name){ System.out.println("这是含参"); } public Dog(String name, int age, double price) { this.name = name; this.age = age; this.price = price; System.out.println("这是全参"); } }
(2)重写@Override
继承后,如果子类对父类的方法不满意,可以重写父类的方法
重写的规则:两同 两小 一大
* 一大:要求子类方法的修饰符 >= 父类的修饰符;
* 两同:子类方法的方法名与参数列表必须要与父类的方法保持一致;
* 两小:子类方法的返回值类型必须与父类的返回值一致,或者是其子类;
* 比如:父 void 子 void ;父Animal 子 Cat都是可以的;
重写和重载其实没多大关系;
/**本类用于测试方法的重写*/ public class TestExtends { public static void main(String[] args) { Father f = new Father(); Son s = new Son(); f.eat(); f.play(); s.eat(); s.play(); } } class Father{ public void eat(){ System.out.println("喜欢吃肉~"); } public void play(){ System.out.println("喜欢放风筝~"); } } class Son extends Father{ /** * 继承后,如果子类对父类的方法不满意,可以重写父类的方法 * 重写的规则:两同 两小 一大 * 一大:要求子类方法的修饰符 >= 父类的修饰符 * 两同:子类方法的方法名与参数列表必须要与父类的方法保持一致 * 两小:子类方法的返回值类型必须与父类的返回值一致,或者是其子类 * 比如:父 void 子 void ;父Animal 子 Cat都是可以的 * */ @Override //只有重写的方法才可以加此注释 public void eat(){ System.out.println("儿子喜欢吃蔬菜~"); } public void play(){ System.out.println("喜欢玩lol"); } }
(3)重载和重写的比较
重载:在一个类中的现象:同一个类中,存在方法名相同,参数列表不同的方法;
重写:建立了继承关系后,子类对父类的方法不满意,可以重写,遵循两同、两小、一大原则
重载的意义:是为了外界调用方法时方便,不管传入什么样的参数,都可以匹配到对应的同名方法
重写的意义:在不修改码源的情况下,进行功能的修改与拓展(OCP原则:面向修改关闭,面向拓展开放)
定义类:定义属性
使用private关键字封装属性;
提供对应的获取属性值与修改属性值的公共方法;
注意:被private修饰的资源是私有资源,只能在本类中使用;
前提:为了保证数据的安全,也为了程序的使用者能够按照我们预先设计好的方式来使用资源;
封装属性:用private修饰的属性;
封装方法:用private修饰的方法,这种方法只能在本类中使用,所以在本类的公共方法里调用私有方法。(外界想用就调用这个公共方法就可以)
提高程序的复用性,减少代码的冗余。
1).通过extends关键字建立子类与父类的继承关系,格式:子类 extends 父类;
2).java的类只支持单继承,一个子类只能有一个父类,但是一个父类可以有多个子类;
3).继承相当于子类将父类的功能复制了一份,继承具有传递性;
4).子类可以拥有自己独有的方法,实现了功能的拓展;
5).子类继承父类以后,可以使用父类的所有非私有资源;注意:私有资源不可以使用的原因,不是没有继承过来,而是被private修饰,访问受限;
6).继承是一种is a 的关系,比如小猫是一只动物,MiaoMiao是一只小猫,继承要求子类必须是父类的一种下属类型,依赖性非常强,强耦合(藕断丝连,看到一个就能看到其他的)。
/**本类用作继承的入门案例*/ public class TestExtends { public static void main(String[] args) { Animal a1 = new Animal(); Cat c1 = new Cat(); MiaoMiao m1 = new MiaoMiao(); a1.eat(); c1.eat(); m1.eat(); m1.studyJava(); } } /** * 1.通过extends关键字建立子类与父类的继承关系,格式:子类 extends 父类 *2.java的类只支持单继承,一个子类只能有一个父类,但是一个父类可以有多个子类 * 3.继承相当于子类将父类的功能复制了一份,继承具有传递性 * 4.子类可以拥有自己独有的方法,实现了功能的拓展 * 5.子类继承父类以后,可以使用父类的所有非私有资源;注意:私有资源 * 不可以使用的原因,不是没有继承过来,而是被private修饰,访问受限 * 6.继承是一种is a 的关系,比如小猫是一只动物,MiaoMiao是一只小猫 * 继承要求子类必须是父类的一种下属类型,依赖性非常强,强耦合 * * */ //爷爷类 class Animal{ public void eat(){ System.out.println("小动物吃什么都行~"); } } //爸爸类 class Cat extends Animal{ int a = 10; private int b = 10; } //孙子类 class MiaoMiao extends Cat{ public void studyJava(){ System.out.println("正在学java~"); System.out.println(a); // System.out.println(b); } }
多态的要求:继承+重写
1).是指一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态;在不同时刻,代表的对象是不一样的,指的是对象的多种形态。
eg:Animal a= new Cat(); Animal b = new Dog();
可以把不同的子类对象当作父类对象来看,进而屏蔽不同的子类对象之间的差异,写出通用的代码,做出通用的变成,统一调用标准。
2)特点:
多态的前提是继承+方法的重写
父类引用指向子类对象。Animal a= new Cat()
多态中,编译看左边,运行看右边。
3)优势:
多态可以让我们不用关心某个对象到底具体是什么类型,就可以使用该对象的某些方法;
提高了程序的可扩展性和可维护性。
4)多态的使用
前提:多态对象把自己看作是父类类型
成员变量:使用的是父类的
成员方法:由于存在重写现象,所以使用的就是子类的
随着类的加载而加载,谁调用就返回谁的。
/**本类用于多态的入门案例*/ public class TestDemo { public static void main(String[] args) { Animal a = new Animal(); Cat c = new Cat(); Dog d = new Dog(); a.eat(); c.eat(); d.eat(); c.jump(); d.run(); // a.jump(); //①父类引用指向子类对象: // 创建出来的子类对象的地址值,交给父类类型的引用类型变量来保存 Animal a2 = new Cat(); Animal a3 = new Dog(); //②编译看左边,运行看右边:必须要在父类中定义这个方法,才能通过编译,因为多态对象认为自己是父类类型 // * 必须要在子类重写父类提供的方法,才能满足多态,因为实际干活的是子类 a2.eat(); a3.eat(); // a2.jump(); // a2.run(); } } /**1.多态的前提:继承+重写 *2.父类对象不可以使用子类的特有功能 * 3.多态对象:①父类引用指向子类对象:创建出来的子类对象的地址值,交给父类类型的引用类型变量来保存 * ②编译看左边,运行看右边:必须要在父类中定义这个方法,才能通过编译,因为多态对象认为自己是父类类型 * 必须要在子类重写父类提供的方法,才能满足多态,因为实际干活的是子类 * */ class Animal{ public void eat(){ System.out.println("小动物Animal吃啥都行~~"); } } class Cat extends Animal{ @Override//重写 public void eat(){ System.out.println("小猫爱吃小鱼干!"); } //特有 public void jump(){ System.out.println("小猫Cat跳的老高了~~"); } } class Dog extends Animal{ @Override public void eat(){ System.out.println("小狗爱吃骨头!"); } public void run(){ System.out.println("小狗Dog跑的老快了"); } }
在java中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以通过重写父类中的方法加以扩展。在这个过程中,就存在多态的应用,存在着两种转型方式:向上转型和向下转型
向上转型:可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用的标准。eg:Father f = new Sun();
说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,了如果子类重写父类中声明过的方法,方法体执行的就是子类重写过后的功能,但是此时的对象是把自己看作是父类类型的,所以其他的资源使用的还是父类的。
向下造型:子类的引用指向子类的对象,过程中必须要采取的强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象。Father p = new Sun();//向上转型,此时P是父类; Sun s = (Sun)p时,把父类的p转成小类型的p,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的。
说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
位置:类里方法外。
执行时机:每次创建对象时,都会执行构造代码块,并且优先于构造方法执行;
作用:用于提取所有构造方法的共性功能。
位置:方法里
执行时机:调用本局部代码块所处的方法时才会执行;
作用:用于控制变量的控制范围,变量的作用范围越小越好。
执行顺序 :构造代码块->构造方法->普通方法—>局部代码快
1).当创建对象时,会触发构造函数;
2).创建对象时,也会触发构造代码块,并且构造代码块优先于构造方法执行;
3).我们创建好对象后,才能通过对象调用普通方法;
4).如果普通方法里有局部代码块,才会触发对应的局部代码块。
注意:被static修饰的代码块称为静态代码块,在类加载时就加载,并且只会被加载一次,一般用于项目的初始化。
/**本类用于测试代码块 * 执行顺序 :构造代码块->构造方法->普通方法—>局部代码快 * 1.当创建对象时,会触发构造函数 * 2.创建对象时,也会触发构造代码块,并且构造代码块优先于构造方法执行 * 3.我们创建好对象后,才能通过对象调用普通方法 * 4.如果普通方法里有局部代码块,才会触发对应的局部代码块 * */ public class TestBlock { public static void main(String[] args) { Pig p1 = new Pig(); p1.play(); p1.eat(); } } class Pig{ String food;//食物 int age;//年龄 /*构造代码块:{} * 1.位置:;类里方法外 * 2.执行时机:每次创建对象时,都会执行构造代码块,并且优先于构造方法执行; * 3.作用:用于提取所有构造方法的共性功能 * */ { System.out.println("这是一个代码块"); } public Pig(){ System.out.println("无参构造~"); } public Pig(int n){ System.out.println("含参构造11~"); } public Pig(String s){ System.out.println("含参构造22~"); } public void play(){ System.out.println("喜欢跑"); } public void eat(){ System.out.println("小猪爱吃菜叶~"); { System.out.println("这是一个局部代码块"); } /**局部代码块{} * 1.位置:方法里 * 2.执行时机:调用本局部代码块所处的方法时才会执行 * 3.作用:用于控制变量的控制范围,变量的作用范围越小越好 * */ } }
this表示当前的对象,this可以调用方法、调用属性、指向对象本身。
(1)当成员变量与局部变量同名时,可以使用this指定本类的成员变量;如果不使用this指定,打印的就是就近的这个局部变量,就近原则。
(2)在无参构造中,调用本类含参构造的功能,this关键字调用构造函数,必须写在构造函数的第一行;
注意:构造函数的调用是单向的,不能来回双向调用,会死循环。
/**本类用于测试关键字this的用法*/ public class TestThis { public static void main(String[] args) { Dog d = new Dog(); Dog d1 = new Dog("旺财"); } } class Dog{ int age = 50; public Dog(){ /**在无参构造中,调用本类含参构造的功能 * this关键字调用构造函数,必须写在构造函数的第一行 * 注意:构造函数的调用是单向的,不能来回双向调用,会死循环 * */ this("大白"); System.out.println("无参构造"); } public Dog(String s){ int count = 666; int age = 5; /**当成员变量与局部变量同名时,可以使用this指定本类的成员变量 * 如果不使用this指定,打印的就是就近的这个局部变量,就近原则*/ System.out.println(this.age);//50 System.out.println(age);//5 /**在含参构造中,调用本类无参构造的功能 * */ // this();//调用本类中的无参构造 System.out.println("含参构造:"+s); } } /**本类用于测试关键字this的用法2*/ public class TestThis2 { public static void main(String[] args) { Dog d = new Dog(); Dog d1 = new Dog("旺财"); } } class Dog{ int age = 50; public Dog(){ /**在无参构造中,调用本类含参构造的功能 * this关键字调用构造函数,必须写在构造函数的第一行 * 注意:构造函数的调用是单向的,不能来回双向调用,会死循环 * */ this("大白"); System.out.println("无参构造"); } public Dog(String s){ int count = 666; int age = 5; /**当成员变量与局部变量同名时,可以使用this指定本类的成员变量 * 如果不使用this指定,打印的就是就近的这个局部变量,就近原则*/ System.out.println(this.age);//50 System.out.println(age);//5 /**在含参构造中,调用本类无参构造的功能 * */ // this();//调用本类中的无参构造 System.out.println("含参构造:"+s); } }
可以把super看作是父类的对象: Father super = new Fasther();
(1).当父类的成员变量与子类的变量同名时,使用super指定父类的成员变量;
(2).使用super在子类构造方法的第一行调用父类的构造方法的功能;
super()是调用父类的无参构造函数;
super(参数)是调用父类对应参数的构造方法。
注意:无论什么情况,super方法的调用的位置都必须是第一行。
/**本类用于测试super的用法2 * * 1.子类在创建对象时,会默认先调用父类的构造方法 * 2.原因是子类构造函数中的第一行默认存在super();--表示调用父类的无参构造 * 3.当父类没有无参构造时,可以通过super(参数);手动调用父类的其他含参构造 * 注意:子类必须调用父类的一个构造函数,不管是无参还是含参,选一个即可 * 4.super调用构造函数要求必须在构造函数的第一行! * 5.构造方法不可以被继承,原因:因为构造函数的要求方法名必须是本类的类名 * 不能在子类中出现一个父类名字的构造方法 * * */ public class TestSuper { public static void main(String[] args) { // Father2 f2 = new Father2(); // Father2 f3 = new Father2(8); Sun2 s2 = new Sun2(); // Sun2 s3 = new Sun2(33); } } class Father2{ public Father2(){ System.out.println("这是父类的无参构造"); } public Father2(int n){ System.out.println("这是父类的含参构造:"+n); } } class Sun2 extends Father2{ public Sun2(){ super(5); System.out.println("这是子类的无参构造"); } // public Sun2(int m){ // System.out.println("这是子类的含参构造:"+m); // } }
(1)this代表的是本类对象的引用,可以把this看作是Cat this = new Cat();
super看作是父类的对象: Father super = new Fasther();
(2)当本类的成员变量与局部变量同名是,需要用 this.变量名 指定本类的成员变量;
当本类的成员变量与父类的成员变量同名时,需要使用 super. 变量名 指定父类的成员变量;
(3)this可以实现调用本类构造方法的功能,不能互相调用,需要写在构造方法首行;
this()表示调用本类的无参构造函数, this(参数)表示调用本类对应参数的含参构造;
super也可以实现调用父类构造方法的功能
super()表示调用父类的无参构造 super(参数)表示调用父类对用参数的含参构造;
注意:
static 静态关键字,可以用来修饰成员变量和方法,
特点:
①static可以修饰成员变量和方法;
②被static修饰的资源称为静态资源;
③静态资源随着类的加载而加载,最先加载,优先与对象进行加载;
④静态资源可以通过类名直接调用,也被称为类资源;
⑤静态被全局所有对象共享,值只有一份;
⑥静态资源只能调用静态资源;
⑦静态区域内不允许使用this和super。
/**本类用于静态static的入门案例 * 0.被static修饰的资源统称为静态资源 * 静态资源是随着类加载而加载到内存中的,比对象优先进入内存 * 所以静态资源可以不通过对象,直接通过类名调用 * */ public class TestStatic1 { public static void main(String[] args) { Fruit.clean(); Fruit f = new Fruit(); f.grow(); f.clean(); } } class Fruit{ /* * 1.可以用static修饰成员变量; * 2 * 3.静态资源在内存中只有一份,而且会被全局所有对象共享 * 所以:不管我们使用那种方式修改静态变量的值,使用任何方式来看,都是 * 静态变量那个刚刚修改了的值。 */ static String kind;//品种 double weight;//重量 public static void clean(){ System.out.println("洗水果~"); } public void grow(){ System.out.println("很好吃~"); } } /*******************************************************************/ /**本类用于测试静态类之间的调用关系 * 总结:1.普通资源可以调用普通资源,也可以调用静态资源 * 2.静态资源只能调用静态资源。 * */ public class TestStatic2 { public static void main(String[] args) { Teacher.ready(); Teacher.age = 10; Teacher t = new Teacher(); System.out.println(t.age); t.age = 100; System.out.println(t.age); } } /** * 1.普通资源能否调用静态方法---可以 * 2.静态资源能否调用普通资源---不可以 * 3.静态资源能否调用静态资源---可以 * */ class Teacher{ String name; public void teach(){ System.out.println("正在授课....."); System.out.println(age); ready(); } static int age ; public static void ready(){ System.out.println("正在备课中....."); } public static void ready2(){ System.out.println(age); ready(); } }
final可以修饰类、方法、字段。
作用:在继承中,子类可以更改父类的功能,当父类功能不许子类改变时,可以利用final关键字修饰父类。
特点:
①被final修饰的类,不能继承;
②被final修饰的方法,不能重写;
③被final修饰的字段是个常量,值不能被修改,且常量命名都大写;
④常量的定义形式:final 数据类型 常量名 = 值;
/**本类用于测试final关键字*/ public class TestFinal { } /*1.final可以用来修饰类,被final修饰的类是最终类,不可以被继承 可以把final修饰的类看成是树结构中的叶子节点 **/ //final class Father2{ class Father2{ /**2.final可以用来修饰方法,被final修饰的方法是这个方法的最终实现,不可以被重写 * */ public void work(){ System.out.println("在工厂上班~~~"); } } class Son2 extends Father2{ /**3.被final修饰的是常量,常量的值是不可以被修改的 * 注意:常量的名称必须是全大写,单词之间使用下划线分隔 * 注意:常量的定义,不管是成员位置还是局部位置,必须手动初始化(赋值) * */ final int CAR_AGE = 18; // final int c = 88; @Override//这是一个重写的方法 public void work(){ System.out.println("在互联网大厂上班"); } }