Java面向对象的三大特征!!!
封装
概念:将类的某些信息隐藏
在类的内部,不允许外部程序直接访问
,而是通过该类提供的方法来实现对隐藏信息的操作和访问(总结:控制)
案例一:将类中属性私有化
package day7.test3;
public class Test3 {
private String name;//将名字私有化,使用方法来控制
private int age;
//在本类中使用方法对私有的name进行初始化,方便控制name的初始化
public void setName(String naem){
if(name.length()<10){
this.name=name;
}
}
public void setAge(int age) {
if(age>=0&&age<150){
this.age=age;
}
}
public String getName(){
return name;
}
public int getAge() {
return age;
}
}
package day7.test3;
public class Test3Test {
public static void main(String[] args) {
Test3 a=new Test3();
a.setName("张三");
System.out.println(a.getName());
}
}
案例二:将某种方法私有化
java设计模式(模板,固定套路)
解决一类问题的固定方式
单例模式,在系统中执行让某个类只创建一个对象
package day7.test4;
public class Test4 {
static Test4 test4=null;
private Test4(){
}
//单一对象,只创建一个对象
public static Test4 getTest4(){
if(test4==null){
test4=new Test4();
}
return test4;
}
}
package day7.test4;
import OOPUnderstand.Test;
public class Test4Test {
public static void main(String[] args) {
Test4 a=Test4.getTest4();
Test4 b=Test4.getTest4();
System.out.println(a);
System.out.println(b);
}
}
继承
概念:Java中,继承是一种面向对象编程的特性,允许一个类(称为子类)继承另一个类(称为父类)的属性和方法。通过继承,子类可以获得父类的属性和方法,同时可以添加新的属性和方法或修改现有方法。这种关系体现了“is-a”关系,即子类是父类的一种特殊类型。
-
子继承父,子就可以拥有父亲的功能
-
可以将这些共性的属性和行为抽取,这样就不需要在每个类中定义同样属性和行为,只需要类与类之间建立联系即可
好处:
- 减少代码的
冗余
,提高了代码的复用性
, - 有利于功能的
扩展
使用环境
- 满足is-a关系,即什么是什么(猫是动物,狗是动物)
继承语法
通过extends关键字,可以声明另一个A
[修饰符]class 类A{
......
}
[修饰符]class 类B extends 类A{
......
}
子类
会继承父类
所有的实例变量
和实例方法
子类
不能直接访问父类
中私有的(private)的成员变量
和方法
- 在Java中,继承的关键字用的是
"extends"
,表示子类
是对父类的扩展
- Java支持多层
继承
(继承体系) - 一个
父类
可以拥有多个子类
- Java只支持
单继承
,不支持多重继承
Java中如果一个类没有使用extends关键字显示的继承其他类,那么这个类默认继承Object类
所以Java中所有的类都直接或间接的继承了Object类
注:父类不可以使用子类功能
方法的重写
当父类中的方法功能实现不能满足子类需求时,可以对方法进行重写(Override
)
子类可以对从父类中继承的方法来进行改造,在程序执行时,子类方法将覆盖父类方法,我么称为 方法的重写 也称方法的覆盖
注:构造方法,静态方法不能重写,成员变量不存在重写
方法重写的规则
-
方法名必须和父类相同,参数列表相同(否则调用的是父类方法)
-
方法返回值类型与父类保持一致
即子类重写方法结构与父类一致
-
子类方法使用的访问权限不能小于父类被重写的访问权限
注意:1.父类私有方法不能重写 2.跨包的父类默认权限的方法也不能重写
-
子类方法抛出的异常不能大于父类被重写方法的异常
@Override使用说明
@Override
是java中定义的注解标签,用来进行标记(进阶部分细讲)写在方法上面,表示此方法是从父类重写而来,用来检测是不是满足重写方法的要求。
这个注解就算不写,只要格式满足要求,也是正确的方法覆盖重写。建议保留,这样编译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰的知道这是一个重写的方法
帮助进行语法检测
- 例如
//父类
public class Cuiweiyang {
String name;
String sex;
//public static abstract void setName(String name);不能使用static调用
public void eat() {
}
public void setName(String name) {
}
public void abst() {
}
public void sleep(){
System.out.println("父亲睡觉");
}
}
//子类
public class Dengqinwen extends Cuiweiyang {
public void eat(){
System.out.println("吃饭");
}
@Override
public void sleep() {
System.out.println("儿子睡觉");
}
@Override
public void setName(String name) {
}
@Override
public void abst() {
}
public Dengqinwen(){
}
}
super关键字
java类中使用super
来调用父类中的操作
-
用于访问父类中定义的属性
-
调用父类中定义的成员方法
-
用于在子类构造器中调用父类构造器
-
@Override public void setName(String name) { super.setName("123"); }
注意:
- 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super
的追溯不仅限于直接父类还可以是父类的父类super
和this
的用法相像,this代表本类对象的引用,super代表父类的内存空间识的标
误区:不要把super
误认为识父类对象
,在创建子类对象
时,不会创建父类对象
,只会将父类
中的信息加载到子类对象
中存储
继承中的构造方法
- 子类继承父类时,不会继承父类的构造方法。只能通过“super(形参列表)”的方式调用父类指定的构造方法。
- 规定super(形参列表),必须声明在构造器的首行。
- 如果在子类构造器的首行没有显示调用super(形参列表),则子类此构器
默认调用super(),即调用父类中空参的构造器。 - 这么做是为了保证先对父类成员进行初始化
开发中常见错误
- 如果子类构造器中既未显示调用父类或本类的构造器,且父类中又没有空参构造器,则编译错误
父类
package day7.extend;
public class Animal {
private String name;
private int age;
public Animal(){
System.out.println("父类无参初始化");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;//this指当前对象
}
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 void eat(){
System.out.println("eat thing");
}
}
子类1
package day7.extend;
public class Cat extends Animal {
public Cat(){
//super();
System.out.println("子类无参初始化");
}
public Cat(String name, int age) {
super(name, age);
System.out.println("有参构造");
}
@Override
public void eat(){
System.out.println("eat cat's food");
}
}
class TestAnimal {
public static void main(String[] args) {
Cat mm = new Cat();
System.out.println("年龄" + mm.getAge());
System.out.println("名字" + mm.getName());
mm.eat();
}
}
运行结果:
无参构造有无super()结果都是一样的,不过推荐写上super()
package day7.extend;
public class Cat extends Animal {
public Cat(){
super();
System.out.println("子类无参初始化");
}
public Cat(String name, int age) {
//super(name, age);
System.out.println("有参构造");
}
@Override
public void eat(){
System.out.println("eat cat's food");
}
}
class TestAnimal {
public static void main(String[] args) {
Cat mm = new Cat("咪咪",2);
System.out.println("年龄" + mm.getAge());
System.out.println("名字" + mm.getName());
mm.eat();
}
}
注释掉super(name,age)
,后发现虽然调用子类中有参构造,但运行结果依然是未初始化的成员变量
package day7.extend;
public class Cat extends Animal {
/**
* chidongxi
*/
public Cat(){
super();
System.out.println("子类无参初始化");
}
public Cat(String name, int age) {
super(name, age);
System.out.println("有参构造");
}
@Override
public void eat(){
System.out.println("eat cat's food");
}
}
class TestAnimal {
public static void main(String[] args) {
Cat mm = new Cat("咪咪",2);
System.out.println("年龄" + mm.getAge());
System.out.println("名字" + mm.getName());
mm.eat();
}
}
取消掉注释后,加上super(name,age)
,就可以调用父类的有参构造方法对成员变量进行了初始化
注意,不能把子类构造方法中默认会调用父类无参构造方法,如果需要显示的使用super调用,必须放在构造方法的第一行,还可以调用父类中指定的构造方法
抽象类
一个类中没有足够的信息来描绘一个具体的对象,这样的类就是抽象类
被abstract类关键字修饰的类,可能会出现抽象方法
注:抽象类除了不能实例化对象外,类的其他功能依然存在,成员变量,成员方法和构造方法,且没有抽象变量这一说法
特点:
- 抽象类不能被实例化(不能创建对象),但可以有构造方法,因为抽象类中含有无具体实现的方法所以不能用抽象类创建对象
- 抽象类只能用做基类,表示的是一种关系,继承抽象类的非抽象类必须实现其中的所有抽象方法,而已实现方法的参数,返回值要和抽象类中的方法一样,否则,该类也必须声明为抽象类
[访问权限符] abstract class 类名{
}
public abstract class Abstract{
}
抽象方法
抽象方法是一种特殊的方法:它只有声明,而没有具体的实现.
[权限修饰符] abstract [返回值类型] 方法名 (参数列表);
public abstract void setName(String name);
抽象方法必须用abstract
关键字进行修饰
注:不可以抽象构造方法
抽象方法是为了子类对其的重写,所有不能使用static
修饰..被static
修饰的为静态方法是可以通过类名直接调用的,但是抽象方法没有任何具体的实现,所以是错误的(静态的方法是不可以被重写的)
多态
多种状态
父类的引用指向子类对象,从而产生多种形态
Animal dog = new Dog();//这里Dog是Animal子类
Animal cat = new Cat();
同一种事物,在不同时刻表现不同状态
二者之间存在直接或间接的继承关系时,父类引用指向子类的对象,即形成多态
- 在编译期类型是父类,运行期类型是子类时,被称为父类引用指向子类对象
class Animal{
....
}
class Cat extends Animal{
.....
}
class Dog extends Animal{
}
Animal cat=new Cat();
Animal dog=new Dog();//Animal的引用指向Dog的对象
向上转型
package homeworkday7.chouxiang;
public abstract class Cuiweiyang {
String name;
String sex;
//public static abstract void setName(String name);不能使用static调用
public abstract void eat();
public abstract void setName(String name);
public abstract void abst();
}
package homeworkday7.chouxiang;
public class Dengqinwen2 extends Cuiweiyang {
public void eat(){
System.out.println("喝水");
}
@Override
public void setName(String name) {
}
@Override
public void abst() {
}
}
package homeworkday7.chouxiang;
public class Test {
public static void main(String[] args) {
Cuiweiyang dqw=new Dengqinwen();
Cuiweiyang dqw2=new Dengqinwen2();
dqw.eat();//编译期间调用的是父类eat,运行期间运行的是子类eat方法
}
}
编译期间是看父类
,运行期间切记是在运行子类
方法,就算子类无,父类有,也是运行子类所继承的方法,绝对不是运行父类方法
多态的好处:提高代码的扩展性
class Animal{
public void eat(){}
....
}
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
.....
}
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
.....
}
class Test{
public static void main(String[]args){
Animal cat=new Cat();
Animal dog=new Dog();//Animal的引用指向Dog的对象
}
}
class Person{
public void Feed(Animal animal){
animal.eat;//此时传递的参数是子类对象的地址
}
//避免了下面的繁琐,提高代码扩展性
public void Feed(Dog dog){
dog.eat;
}
public void Feed(Cat cat){
cat.eat;
}
}
多态环境下对成员方法(非静态)的调用
如上,
cat.eat();
//调用的是子类中的方法
简单来说就是:编译看左边,运行看右边
多态环境下对静态成员方法的调用
package homeworkday7.chouxiang2;
class Animal {
public static void eat(){
System.out.println("吃食物");
}
}
class Cat extends Animal{
public static void eat(){
System.out.println("猫吃鱼");
}
}
class Test{
public static void main(String[] args) {
Animal cat=new Cat();
cat.eat();//调用的是Animal的方法
}
}
简单的说:编译运行都看左边
注意:变量不存在被子类覆盖写这一说法,只有方法存在覆写
为了实现多态性,我们将子类类型向上转为了父类类型.但是一旦类型上升为父类类型,那么就调用不到子类特有的方法
解决办法:
就行向下转型,把父类转换为子类类型
向下转型(强制类型转换)
格式 [类型]对象名1 = (类型)对象名2
将后面大的父类对象转换为子类小的对象
注:同父类的子类不能相互转化
package homeworkday7.chouxiang;
public class Test {
public static void main(String[] args) {
Cuiweiyang dqw=new Dengqinwen();
Cuiweiyang dqw2=new Dengqinwen2();
Person zs = new Person();
dqw.sleep();
dqw2.sleep();
Dengqinwen dqw3=(Dengqinwen) dqw;//把Cuiweiyang类型强制转化为子类的Dengqinwen类型
zs.feed(dqw3);
}
}
Dengqinwen2 dqw4=(Dengqinwen2) dqw3;//错误,同父类的子类不能相互转化
优缺点
优点
- 灵活性:多态使用使代码更加灵活,可以通过父类类型的引用变量来引用不同子类对象,从而实现不同对象的统一操作
- 可扩展性:通过多态,我们可以方便地扩展和添新的子类,而不需要修改已有的代码
- 代码重用:多态可以提高代码的重用性,通过定义父类类型的方法,可以在不同的子类中进行具体实现,减少了代码的冗余
缺点
- 性能损失:多态需要在运行时进行动态绑定和方法调用,这会带来一定的性能损失,相比于直接调用子类方法,多台调用会稍微慢一些
- 可读性降低:由于多态的特性,代码的行为可能会依赖于运行时的实际对象类型,这可能会增加代码的复杂性和难以理解程度
- 限制:多态只能通过父类类型的引用变量来引用子类对象,而不能直接访问子类特有的方法和属性,如果需要访问子类特有的功能,需要进行类型转换
本文来自博客园,作者:Yang0710,转载请注明原文链接:https://www.cnblogs.com/cwyYYDS/p/18213965