Java中面向对象的主要问题

1.类与对象的关系

什么是类?

类就是具备某些共同特征的实体的集合,它是一种抽象的数据类型,它是对所具有相同特征实体的抽象。在面向对象的程序设计语言中,类是对一类“事物”的属性与行为的抽象。

什么是对象?

(1) 对象就是一个真实世界中的实体,对象与实体是一一对应关系的,意思就是现实世界的每一个实体都是一个对象,所以对象是一个具体的概念。

(2) 类是对象的一个集合,对象是类的实例。

(3) 而对象的产生在Java中是使用new来实现的。

假设:做一个比方老师是对一个群体的称呼,老师这个群体就是一个类,而老师又可以具体到某一个人,比如张老师、王老师之类的等等,张老师就是一个对象。

类和对象的区别

(1) 类是一个抽象的概念,它不存在于现实中的时间/空间里,类只是为所有的对象定义了抽象的属性与行为。就好像“Person(人)”这个类,它虽然可以包含很多个体,但它本身不存在于现实世界上。

(2) 对象是类的一个具体。它是一个实实在在存在的东西。

(3) 类是一个静态的概念,类本身不携带任何数据。当没有为类创建任何对象时,类本身不存在于内存空间中。

(4) 对象是一个动态的概念。每一个对象都存在着有别于其它对象的属于自己的独特的属性和行为。对象的属性可以随着它自己的行为而发生改变。

2.为什么要封装

(1) 提高了安全性

(2) 提高了复用性

(3) 隐藏了实现细节

总结为八个字:隐藏属性,暴露方法

Java中4种“访问控制符”分别为:private、default、protected、public,他们说明了面相对象的封装性。

3.为什么要继承,满足什么样的条件

继承的概念:继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

子类重写(覆盖)父类的方法必须满足的条件:

1.父类中的方法在子类中必须可见,即子类继承了父类中的该方法(可以显式的使用super关键字来访问父类中的被重写的方法), 如果父类中的方法为private类型的,那么子类则无法继承,也无法覆盖。

2.子类和父类的方法必须是实例方法,如果父类是static方法而子类是实例方法,或者相反都会报错。 如果父类和子类都是static方法,那么子类隐藏父类的方法,而不是重写父类方法。

3.子类和父类的方法必须要具有相同的函数名称、参数列表,并且子类的返回值与父类相同或者是父类返回类型的子类型(jdk1.5之后)。 如果方法名称相同而参数列表不同(返回类型可以相同也可以不同),那么只是方法的重载,而非重写。 如果方法名称和参数列表相同,返回值类型不同,子类返回值类型也不是父类返回值类型的子类,编译器就会报错。

4.子类方法的访问权限不能小于父类方法的访问权限(可以具有相同的访问权限或者子类的访问权限大于父类)。 访问权限由高到低:public、protected、包访问权限、private。如果子类方法的访问权限低于父类,则编译器会给出错误信息

5.子类方法不能比父类方法抛出更多的编译时异常(不是运行时异常),即子类方法抛出的编译时异常或者和父类相同或者是父类异常的子类。

4.什么是多态

多态是同一个行为具有多个不同表现形式或形态的能力。

多态存在的三个必要条件

继承
重写
父类引用指向子类对象

多态的优点

1.消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。

5.什么是方法重载

重载发生在本类,方法名相同,参数列表不同,与返回值无关,只和方法名,参数列表,参数的类型有关.

重载(Overload):首先是位于一个类之中或者其子类中,具有相同的方法名,但是方法的参数不同,返回值类型可以相同也可以不同。

(1):方法名必须相同

(2):方法的参数列表一定不一样。

(3):访问修饰符和返回值类型可以相同也可以不同。

其实简单而言:重载就是对于不同的情况写不同的方法。 比如,同一个类中,写不同的构造函数用于初始化不同的参数。

public class Test1 {
    public void out(){
        System.out.println("参数"+null);
    }
    //参数数目不同
    public void out(Integer n){
        System.out.println("参数"+n.getClass().getName());
    }
 
    //参数类型不同
    public void out(String string){
        System.out.println("参数"+string.getClass().getName());
    }
 
    public void out(Integer n ,String string){
        System.out.println("参数"+n.getClass().getName()+","+string.getClass().getName());
    }
    //参数顺序不同
    public void out(String string,Integer n){
        System.out.println("参数"+string.getClass().getName()+","+n.getClass().getName());
    }
    
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        test1.out();
        test1.out(1);
        test1.out("string");
        test1.out(1,"string");
        test1.out("string",1);
    }
}

6.什么是方法重写

重写发生在父类子类之间,比如所有类都是继承与Object类的,Object类中本身就有equals,hashcode,toString方法等.在任意子类中定义了重名和同样的参数列表就构成方法重写.

重写(override):一般都是表示子类和父类之间的关系,其主要的特征是:方法名相同,参数相同,但是具体的实现不同。

重写的特征:

(1):方法名必须相同,返回值类型必须相同

(2):参数列表必须相同

(3):访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。

(4):子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。

(5):构造方法不能被重写,

简单而言:就是具体的实现类对于父类的该方法实现不满意,需要自己在写一个满足于自己要求的方法。

class Test{
    public void out(){
        System.out.println("我是父类方法");
    }
}
 
public class Test1 extends Test{
    @Override
    //方法签名完全一致
    public void out() {
        System.out.println("我是重写后的子类方法");
    }
 
    public static void main(String[] args) {
        Test test = new Test();
        test.out();
        test = new  Test1();
        test.out();
    }
}

7.类的组成结构

属性:对象数据的描述

方法:对象的行为

构造方法:用于实例化对象

内部类:在类中声明的类(inner class)

块:分静态块与实例块

类的声明:(访问权限修饰符public.default(可忽略不写,为默认))(修饰符final.abstract.synchronized)class 类名{ 类体 }

类的作用:类就是一个模板,定义多个对象共同的属性和方法 如:学生类(张三,李四) 手机类(华为.oppo)

8.实例对象的步骤

1.加载父类(以下序号相同,表明初始化是按代码从上到下的顺序来的)

(1) 为父类的静态属性分配空间并赋于初值

(2) 执行父类静态初始化块;

2.加载子类

(1) 为子类的静态属性分配空间并赋于初值

(2) 执行子类的静态的内容;

2.加载父类构造器

(1) 初始化父类的非静态属性并赋于初值

(2) 执行父类的非静态代码块;

(3) 执行父类的构造方法;

4.加载子类构造器

(1) 初始化子类的非静态属性并赋于初值

(2) 执行子类的非静态代码块;

(3) 执行子类的构造方法.

总之一句话,静态代码块内容先执行(父先后子),接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。

9.父类什么样的属性和方法能被继承

(1) Java中继承是单继承,只有一个父类,可以有多个子类。

(2) 子类在继承父类的属性和方法的时候可以扩展自己的属性和方法。

(3) 继承父类的时候,父类必须有一个无参构造。

(4) 子类构造的时候会默认通过super()来调用父类的构造方法。初始化子类的时候,先调用父类的默认构造,再调用子类的构造。

(5) 调用父类的属性或方法可以通过super关键字。

(6) 在调用子类的方法时会先在子类中寻找那个方法,找到后调用成功,否则再去父类中找想要调用的方法。如果在子类中找到了那个方法,则子类重写了父类的方法。

(7) 用final修饰的类不能被继承。

不能被继承的:

(1) 私有的属性(可以通过set get或super方法使用父类的私有属性)

(2) 不同包的friendly

(3) 构造方法不能继承

(4) 类是不继承父类的static变量和方法的。因为这是属于类本身的。但是子类是可以访问的

(5) 类中同名的static变量和方法都是相互独立的,并不存在任何的重写的关系

10.单例模式是什么?单例模式是如何实现的

单例模式(Singleton),也叫单子模式,是一种常用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候,整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,显然,这种方式简化了在复杂环境下的配置管理。

综上所述,单例模式就是为确保一个类只有一个实例,并为整个系统提供一个全局访问点的一种方法。

三要素:

私有的构造方法;

指向自己实例的私有静态引用;

以自己实例为返回值的静态的公有方法。

实现方法

在单线程环境下,单例模式根据实例化对象时机的不同,有两种经典的实现:一种是 饿汉式单例(立即加载),一种是 懒汉式单例(延迟加载)。饿汉式单例在单例类被加载时候,就实例化一个对象并交给自己的引用;而懒汉式单例只有在真正使用的时候才会实例化一个对象并交给自己的引用。

11.抽象类与接口

抽象类

用abstract修饰的类叫做抽象类。

在讲抽象类之前有必要强调一下abstract修饰符:

(1) abstract修饰的类为抽象类,此类不能有对象,(无法对此类进行实例化,说白了就是不能new);

(2) abstract修饰的方法为抽象方法,此方法不能有方法体(就是什么内容不能有);

关于抽象类的使用特点:

(1) 抽象类不能有对象,(不能用new此关键字来创建抽象类的对象);

(2) 有抽象方法的类一定是抽象类,但是抽象类中不一定有抽象方法;

抽象类生来就注定它是要被继承的,如果没有任何一个类去继承它的话,那么也就失去了它的意义;抽象方法生来就是要被重写的,而且是必须重写。(只要继承了某个抽象类,就必须去重写此抽象类中含有的抽象方法)

public abstract class Animal {
 public abstract void eat();
 public abstract void sleep();
}

以上代码是定义了一个叫做animal的抽象类,其中含有eat()和sleep()两个抽象方法。
这里指的注意的是:抽象方法不能有方法体,在方法后面加一个大括号而里面什么都不写也是不行的,编译器会报“abstract methods do not specify a body”这样一个错误。

接口

接口就是一个规范和抽象类比较相似。它只管做什么,不管怎么做。通俗的讲借口就是某个事物对外提供的一些功能的声明,其定义和类比较相似,只不过是过interface关键字来完成

其中重要的几个知识点:

1.接口中的所有属性默认为:public static final ****;

2.接口中的所有方法默认为:public abstract ****;

3.接口不再像类一样用关键字 extends去“继承”,而是用 implements 去“实现”,也就是说类和接口的关系叫做实现,(例如:A类实现了B接口,那么成为A为B接口的实现类。而类与类之间的继承的话,叫做A类继承了B类,其中B类即为A类的父类)。实现接口与类的继承比较相似。

public interface Sleep {
	public static int a = 1;
	public static int b = 2;
	public void ioSleep(int i);
}
public interface Eat {
	public abstract void ioEat();
}
public interface Study {
	public void ioStudy();
}
public class Cat implements Sleep,Eat{
 
	@Override
	public void ioSleep(int i) {
		System.out.println("我是猫,我每天都不用睡觉!!!");
	}
 
	@Override
	public void ioEat() {
		System.out.println("我是猫,我吃猫粮!!!");
	}
public class Person implements Sleep,Eat,Study{
 
	@Override
	public void ioStudy() {
		System.out.println("我是人,我每天都要学习");
	}
 
	@Override
	public void ioEat() {
		System.out.println("我是人,我要吃大鱼大肉还要喝酒");
	}
 
	@Override
	public void ioSleep(int i) {
		System.out.println("我是人,我每天要睡24小时");
	}
}

以上代码定义了三个接口分别是:Study,Eat,Sleep,以及两个类分别是Cat和Person。其中Cat类实现了两个接口分别是:Eat和Sleep,而Person实现了三个接口分别是:Study,Eat,Sleep。这是为什么呢?就是因为Cat并不需要学习,而Person需要学习,所以在Cat类中没有实现Study这个接口,而在Person中却是有的。那么显而易见,接口所阐述的是“有没有”的问题,而刚刚所说的抽象类阐述的是“是不是”的问题。

抽象类和接口的区别

(1) 抽象类要被子类继承,接口要被类实现。

(2) 接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。

(3) 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

(4) 接口是设计的结果,抽象类是重构的结果。

(5) 抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。

(6) 抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。

(7) 抽象类主要用来抽象类别,接口主要用来抽象功能。

发布了19 篇原创文章 · 获赞 0 · 访问量 314

猜你喜欢

转载自blog.csdn.net/weixin_45956838/article/details/103417404