面向对象中的一些概念的理解
今天回顾的内容,可能相对于前面的内容来说在理解上需要花费一点功夫,但面向对象也是属于打基础的部分,学好虽然不能够加分,但是没有掌握到这方面的知识,在后续的基础学习中,会有一些问题。那,我们开始回顾今天的内容吧!!
1. 构造方法
特点:与类同名而没有返回值类型
作用:创建对象
package cn.tedu.object; public class PersonDemo { public static void main(String[] args) { //构造方法 --- 创建对象的 //调用构造方法创建 Person 对象 Person p = new Person("翠花"); p.name = "如花";//对象点出来的是一个成员变量 p.eat(); Person p2 = new Person(); p2.eat(); } } class Person{ String name; int age; char gender; String no; //身份证号,数字,还很多有的人的后面是 x //在类中没有手动指定构造方法的时候,那么在编译的时候会自动添加一个无参的构造方法 //没有返回值类型,与当前类同名 //构造方法中可以写 return,用于规避一些不合常理的数据 public Person(){ } //在代码中手动指定了含参构造,那么就不会再默认添加无参构造 public Person(String name){//开发了网站,用的人都要注册,写一个 user 类,用户的基本信息创建的时候就要写进来 //遵循就近原则 this.name = name; // Warning --- 警告 --- 智能工具编译到这儿的时候认为这个地方可能存在问题, //但是不影响程序的正常运行 } //Person p = new Person("珠子",-18); public Person(String name,int age){ this.name = name; if(age < 0){ return; } this.age = age; } public void eat(){ System.out.println("吃饭"); } }
2. this 关键字
this 关键字在类中调用本类里面的属性和方法,代表当前在活动的对象。
2.1 this 语句 -> this(参数列表) - 表示在本类的构造方法中调用本类对应形式的其他构造方法 - this 语句必须放在构造方法的第一行
package cn.tedu.object; public class ThisDemo { public static void main(String[] args) { Student s1 = new Student("Amy"); System.out.println("s1:" + s1); Student s2 = new Student("Sma"); System.out.println("s2:" + s2); s1.name = "Amyy"; } } class Student{ String name; int age; String no;//学号 //用 this 代替一个具体的对象 //this 代表当前在活动的对象 //this 在本类中调用本类中的属性和方法 public Student(String name){ this.name = name; System.out.println("this:"+this); } public Student(String name, int age){ } public Student(String name, int age, String no){ //this 调用的普通方法而不是构造方法 //this.Student(name,age); //this(参数列表) --- this 语句 //表示调用本类中对应形式的构造方法 //必须放在构造方法的首行 this(name,age); this.no = no; } }
3. 代码块
构造代码块:定义在类中用 { }包起来的代码 --- 在创建
局部代码块:定义在方法中用 { }包起来的代码 --- 提高了栈内存的利用率
package cn.tedu.object; public class CodeDemo { public static void main(String[] args) { Baby b1 = new Baby(); Baby b2 = new Baby("铁蛋"); } } //定义一个代表婴儿的类 class Baby{ String name; //构造代码块/初始化代码块 //无论利用哪个构造方法创建对象,构造代码块都会先于构造方法执行 //如果每一个构造方法都有一些要初始化的操作,可以将它们提取到构造代码块中执行 { this.cry(); this.eat(); } public Baby(){ System.out.println("running~~~"); } public Baby(String name){ // this(); this.name = name; //this.cry(); //this.eat(); } public void cry(){ System.out.println("这个婴儿在哇哇的哭"); } public void eat(){ System.out.println("这个小孩在吃奶"); } }
4. 面向对象的特征
封装、继承、多态(抽象,一些少部分公司会问这个)(而 Java 语言的特征,第一条,简单易学(先讲 C 语言的什么什么,再推导出 java 的简单易学),完全面向对象,安全可靠,可移植性)
4.1 封装
将代码进行提取和总结,抽取成不同的形式 --- 封装
体现形式:方法(利用方法名直接反复调用相同的代码)、类(属性的私有化 - 将属性限制为私有的,然后提供对外的设置和获取的方法,然后在方法中进行限制使数据更加符合常理)
优势:提高了代码的复用性,保证了数据的合法性
权限修饰符
指在 Java 中限定使用范围的关键字
|
本类中
|
子类中
|
同包内中
|
其他类中
|
public
|
可以
|
可以()
|
可以(你的室友)
|
可以
|
protected(你自己的一些东西,你们寝室的扫帚)
|
可以
|
可以
|
可以
|
不可以(其他寝室的,来抢劫啊)
|
默认(没有给定的形式)(你们家的门,或者窗)
|
可以
|
同包子类可以,不同包子类不行
|
可以
|
不可以(你来弄,入室抢劫啊)
|
private(相当于自己的小秘密,私房钱,青春回忆)
|
可以
|
不可以
|
不可以
|
不可以(走在路上一人说给我点钱花呗,不行)
|
关于 Protect 修饰符的一些理解:
package cn.tedu.extendsx.b; import cn.tedu.extendsx.a.A; public class E extends A{ public static void main(String[] args) { D d = new D(); //对象的声明类是 D类,所以d对象所对应的子类是 D类 //因此d对象使用 m方法需要在对应的子类 D类中使用 //子类对象使用的时候必须在对应的子类中使用 //d.m(); } } //public class E{ // public static void main(String[] args) { // D d = new D(); // //虽然D通过继承A可以使用A类中m方法,但是m方法最终定义在A类中 // //m方法的权限是protected:本类中、子类中、同包类中 // //m方法的本类是A类,E和A是本类中? --- 不是 // //m方法是在E类中,E是A的子类吗? --- 不是 // //E类所在的包是b包,A类所在的包是a包,所以E和A是同包类吗1?---不是 // //d.m(); // } //}
练习:定义一个类表示矩形(长方形),提供获取周长和面积的方法
package cn.tedu.fengzhuang; public class Rectangle { public static void main(String[] args) { GetRectangle rec = new GetRectangle(2, 4); System.out.println("周长:"+rec.getLong()+" 厘米,面积:"+rec.getSqure()+" 平方厘米"); } } class GetRectangle{ private double width; private double height; public double getWidth() { return width; } public double getHeight() { return height; } public GetRectangle(double width, double heigth){ if(width <= 0 || heigth == 0) return; this.width = width; this.height = heigth; } public double getLong(){ return 2 * (width + height); } public double getSqure(){ return width * height; } }
4.2 继承
如果一些类中含有相同的代码,那么可以将这些相同的代码提取到一个新的类中,然后通过 extends 关键字让原来的类和新的类产生关系 --- 继承。
通过继承,原来的类就称之为了子类(派生类),新的类就成了父类(超类/基类)。
子类通过继承父类可以使用父类中的一部分方法和属性
注意:子类继承了父类的全部的数据域(方法 + 属性),但是继承之后只有一部分数据域对子类可见。
在 Java 中,支持的是类和类之间的单继承 -> 一个子类只能继承一个父类,但是一个父类可以有多个子类
单继承一定优于多继承吗? --- 不对,C++ 中采用的是多继承,但是现在几十年的时间了,C++ 仍然采用多继承。
---比如,一个人要到一个医院上班(这个 Person 继承这个 Doctor 类(有一个treat() 方法)),要到医院去上课(这个 Person 继承 Teacher 类(有一个 teach() 方法)),如果是单继承,选择继承了 Doctor 了,Teacher 类中的代码就要在 Person 中再写一遍。
---多继承比单继承能够更好的提高代码的复用性
---再比如,如果是多继承,无论是 Doctor 还是 Teacher 都有一个相同名字 salary() 方法(这两个类的计算方法不一样),但是我想知道工资(p.salary() 并不确定是哪一个)
---多继承导致在调用方法的时候可能产生歧义
优势:提高代码的复用性,避免方法的调用产生歧义
4.2.1 Super 关键字
在子类中用于表示父类对象的引用,可以在子类中调用父类中的方法的属性。
super 语句 --- 子类在继承父类之后,子类的构造方法中会含有一个 super 语句。如果没有没有手动指定 super 语句,那么默认使用super()调用父类无参的构造;如果父类只提供了含参构造,那么父类就必须手动提供对应形式的 super 语句 --- super 语句必须在子类构造方法的首行
4.2.2 方法的重写/覆盖
在父子类中存在了方法签名相同的非静态方法。遵循“两等两小一大 ”原则:
1.方法签名相同
2.如果父类中的方法的返回值类型是基本类型/void,那么子类重写的方法的返回值类型与父类一致
3.如果父类中的方法的返回值类型是引用类型,那么子类在重写方法的时候,返回值类型要么与父类一致,要么是父类方法返回值类型的子类
class A{}
class B extends A{}
class C{
public A m(){return null;}
}
class D extends C{
public B m() { return null;}
}
4.子类重写的方法的权限修饰符的范围要大于等于父类中对应方法的权限修饰符的范围
class A{
public void m(){ }
}
class B extends A{
public int m(){ return 0;}
}
注意:如果父类中的方法用 private 修饰,那么这个方法对子类不可见,所以此时与子类中的方法构不成重写
package cn.tedu.extendsx; public class ExtendsDemo { public static void main(String[] args) { Cat c = new Cat(); //通过继承,子类可以使用父类中的属性和方法 c.eat(); } } //父类 class Pet{ String name; String color; public Pet(String name){ } public Pet(String name, String color){ } public void eat(){ System.out.println("在吃东西"); } } //利用extends关键字让原来的类与提取出来的新的类产生了联系 --- 继承 //子类 class Cat extends Pet{ public Cat(){ //在子类构造方法中,如果没有手动指定,那么默认添加super() //super 语句 --- 表示调用父类中对应的形式的构造方法 //super() --- Pet() //如果父类中只提供含参构造,那么子类中就必须手动提供对应形式的构造方法 super("波斯猫"); } public void drink(){ //通过super代表父类对象,通过父类对象调用父类中的属性和方法 super.eat(); System.out.println("吃完东西喝点水"); } public void catches(){ System.out.println("这只猫在挠沙发"); } } //子类 class Dog extends Pet{ public Dog(){ //super("金毛","绿色"); super("二哈");//this();这两个不能够同时出现 super.color = "绿色"; } public void bark(){ System.out.println("这只狗在叫"); } }
4.3 多态
(在未来的基础阶段你感觉不到用处,但在后面的大数据中,解耦会涉及到)一个接口建了之后,实现的具体方式就由其他分别体现(使用子类都统一用一个父类接收,在父类中判断接收的具体对象),两个接口,一个加分,一个加密,子类造型成父类,在父类中判断是不是这个子类,是就加分。编译时,不管子类是什么,后面交给程序员就具体实现相应的类。只有在运行时,便于维护。
4.3.1 编译时多态:方法的重载
add(2,4) -> add(int, int);
add(3,1,7) ->add(int, int, int) //编译时期就知道这个方法有没有
4.3.2 运行时多态:向上造型和方法的重写 --- 基于继承的
注意:如果使用向上造型来创建对象,那么这个对象所能调用的方法看的是父类中的声明,方法如何执行看的是子类中的实现过程
package cn.tedu.duotai; public class DuotaiDemo { public static void main(String[] args) { //父类声明对象,用子类创建对象,利用向上造型来创建的这个对象 //对象在编译过程中并不会检查到底使用的是哪个子类,在编译期间只会检查声明类和实现类之间是否有继承关系, //直到运行的时候才会检查具体的子类然后根据子类来分配空间 Pet p = new Cat(); //p.catches(); //当使用向上造型来创建对象的时候,只能使用父类中声明的方法,而不能使用子类中单独定义的方法 p.eat(); } } class Pet{ public void eat(){ System.out.println("在吃东西~~~"); } } class Cat extends Pet{ @Override public void eat(){ System.out.println("这只猫在吃草~~~"); } public void catches(){ System.out.println("这只猫在抓老鼠"); } } class Dog extends Pet{ @Override public void eat(){ System.out.println("这只狗在吃猫~~~"); } public void bark(){ System.out.println("这只狗在叫"); } }
重写的理解
1.子类重写的方法的权限修饰符的范围要大于等于父类中对应方法的权限修饰符
![](https://images2018.cnblogs.com/blog/1348697/201808/1348697-20180810184753339-166204156.png)
2.如果父类中的方法的返回值类型是引用类型,那么子类在重写方法的时候,返回值类型要么与父类一致,要么是父类方法返回值类型的子类
![](https://images2018.cnblogs.com/blog/1348697/201808/1348697-20180810184823186-1509218461.png)
注意:Java 中所有的基本类型之间没有继承关系,之所以能够自动提升,是因为所表示的范围是否能够包含
package cn.tedu.extendsx; public class OverrideDemo { private void mian() { // TODO Auto-generated method stub Teacher t = new Teacher(); //子类对象是调用重写之后的方法 t.work(); } } //表示职业的类 class Profession{ public void work(){ System.out.println("在工作~~~"); } } class Teacher extends Profession{ //父类中的方法写的比较简单,子类需要对父类中的方法进行扩展 @Override //@ 注解 //@Override 这个注解是用于校验当前方法是否构成了重写 public void work(){ System.out.println("这个老师在诲人不倦"); } }
分类: 大数据