面向对象概述

面向对象概述

一、类与对象

1.类的定义

是用于描述现实事物的,它将现实事物进行抽象化,模板化描述。将事物的特点(属性)和行为封装在其中。

当某一类中共同的特征和行为封装起来,同时把共同特征作为类的属性(也叫成员变量),把共同行为作为类的方法(也叫成员方法)。

2.类的定义格式

[修饰符] class 类名 [extends 父类名][implements 接口名]{
  //类体,包括类的成员变量和成员方法
}

3.声明(定义)成员变量和成员方法

[修饰符] 数据类型 变量名 [=]private String color;
public int number = 20;//成员变量

[修饰符] [返回值类型] 方法名 ([参数类型 参数名1,参数类型 参数名2...]){
  //方法体
  return 返回值 //当返回值类型为void时,return可以省略
}
public class Car{
  int number;
  void run(){
    System.out.println("一共有"+number+"辆车跑起来了");
  }
}

4.创建对象与对象的内存分析及访问对象

类名 对象名称 = new 类名();
Car c = new Car();

在这里插入图片描述

其中Car类型的变量c被存放在栈内存中,它是一个引用,会指向真正的对象,通过new Car()创建的对象则放在堆内存中,这才是真正的对象。

访问对象

public class Demo{
  public static void main(String[] args){
    Car c = new Car();
    c.number = 4;
    c.color = "red";
    c.run;
  }
}

5.局部变量与成员变量的区别

(1).定义位置不同

成员变量定义在类中。

局部变量定义在方法中或者语句里面。

(2).在内存中的位置不同

成员变量存储在内存的对象中。

局部变量存储在内存的方法中。

(3).声明周期不同

成员变量随着对象的出现而出现在堆中,随着对象的消失而从堆中消失。

局部变量随着方法的运行而出现在栈中,随着方法的弹栈而消失。

(4).初始化不同

成员变量因为在堆内存中,所以有默认的初始化值

局部变量没有默认的初始化值,必须手动的给其赋值才可以使用。

6.匿名对象

在main方法中,每次创建对象,只为了调用其run方法,而这时定义的***c***引用变量,几乎没有什么作用。而***c***等价于new Car();这时可以使用简化书写成new Car().run();.

new Car();这行代码并没有给Car对象创建引用,即没有给对象起名字,这样的代码就称之为匿名对象。匿名对象可以简化上述代码。

即可以将上述代码简化为

public class Demo{
  public static void main(String[] args){
    new Car().run;
  }
}

二、类的封装

1.封装的概念及封装的作用

将具体功能封装到方法中,方法也可以封装到类中。也是面向对象思想的特征之一。

封装表现:

  1. 函数就是一个最基本封装体。

  2. 类其实也是一个封装体。

    封装的好处:

    1. 提高了代码的复用性。
    2. 隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。
    3. 提高了安全性。

2.访问控制符

级别控制由小到大:private(同一类中)<default(同一包中)<protected(子类中)<public(全局范围)

class Person 
{
	//私有成员变量
	private int age ;
	private String name;
	//对外提供设置成员变量的方法
	public void setAge(int a)
	{
		//由于是设置成员变量的值,这里可以加入数据的验证
		if( a < 0 || a > 130 )
		{
			System.out.println(a+"不符合年龄的数据范围");
			return;
            //throw new RuntimeException("对不起,您的年龄数值 "+a+" 是非法的。");//异常!一旦出现,程序结束。需要修正代码。
		}
		age = a;
	}
	//对外提供访问成员变量的方法
	public void getAge()
	{
		return age;
	}
}

类中不需要对外提供的内容都私有化,包括属性和行为。

以后再描述事物,属性都私有化,并提供setXxx和getXxx方法对其进行访问。

注意:私有仅仅是封装的体现形式而已。

3.构造函数

构造函数的格式
修饰符 构造函数名(参数列表)
{
  //方法体
}
构造函数的特点
  1. 构造函数没有返回值类型。也不需要写返回值。因为其是为构建对象的,对象创建完,函数就执行结束。
  2. 构造函数名称必须和保持一致。
  3. 构造函数没有具体的返回值。
构造函数的细节

1.一个类中可以有多个构造函数,多个构造函数是以重载的形式存在的。

2.构造函数中也是有return语句的,用于结束初始化动作。

3.构造函数是可以被private修饰的,作用:其他程序无法创建该类的对象。

构造函数和一般函数区别

构造函数在对象创建时就执行了,而且只执行一次

一般函数是在对象创建后,需要使用时才被对象调用,并可以被多次调用。

4.this关键字

java中提供了一个关键字this来指代当前对象,用于在方法中访问对象的其他成员.

class Person{
  int age;
  public Person(int age){
    this.age = age;//将局部变量age的值赋给成员变量age
  }
}

三种不同的使用方式

(1)通过this关键字来调用成员变量,解决与局部变量名称冲突问题。

class Person{
  int age;
  public Person(int age){
    this.age = age;//将局部变量age的值赋给成员变量age
  }
}

(2)通过this关键字调用成员方法

class Person{
  public void openMouth(){
  }
  public void speak(){
    this.openMouth();
  }
}

(3)通过this关键字调用构造方法

class Person{
  public Person(){
    System.out.println("无参的方法");
  }
  public Person(int age){
  	this();
    System.out.println("有参的方法");
  }
}
public class Demo{
  public static void main(String[] args){
    Person p = new Person(18);//在实例化perso对象时,调用了有参的构造方法,在有参的方法中又通过this()调用了无参的构造方法
  }
}

在使用this调用类的构造方法时,注意:

只能在构造方法中使用this调用其他的构造方法,不能在成员方法中使用。

在构造方法中,使用this调用构造方法的语句必须是该方法的第一条执行语句,且只能出现一次。

三、静态变量(static关键字)

1.static关键字

在java中,定义了一个static关键字,它用于修饰类的成员,如成员变量、成员方法以及代码块等,被static修饰的成员具备一些特殊性质。

2.静态变量

在定义一个类时,只是在描述某类事物的特征和行为,并没有产生具体的数据。只有通过new关键字创建该类的实例对象后,系统才会为每个对象分配内存空间,存储各自数据。有时候,开发者会希望某些特定的数据在内存中只有一份,而且能够被一个类的所有实例对象所共享。例如以下图解

在这里插入图片描述

class Student{
  static String className;
}
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student();
        Student s2 = new Student();
        Student.className = "三年级二班";
        System.out.println("我是"+s1.className+"的学生");
        System.out.println("我是"+s2.className+"的学生");
    }
}

3.静态变量和成员变量的区别

静态变量和成员变量的区别:

1.变量所属不同

静态变量所属与类,也称为类变量。

成员变量所属于对象,也称为实例变量(对象变量)。

2.内存中的位置

静态变量存储于方法区中的静态区中。

成员变量存储于堆内存中。

3.在内存中出现的时间

静态变量随着类的加载而加载,随着类的消失而消失。

成员变量随着对象的创建而在堆内存中出现,随着对象的消失而消失。

4.静态方法的使用注意事项

被静态修饰的方法中无法访问非静态的属性和方法的原因

  1. 静态是随着类的加载就加载了。也是随着类的消失而消失了。
  2. 静态优先于对象存在,被对象共享。
  3. 因为静态先存在于内存中无法访问后来的对象的中的数据,所以静态无法访问非静态。而且内部无法书写this。因为这时对象有可能不存在,this没有任何指向。

静态方法使用注意事项:

  1. 静态方法不能访问非静态的成员。但是非静态可以访问静态成员的。

    说明:静态的弊端在于访问出现局限性。好处是可以直接被类名调用。

  2. 静态方法中不允许出现this,super关键字。

main 方法其实也静态的。因为main是程序的入口,是提供给JVM使用的,当在dos中输入java XXX 时,会启动JVM,同时JVM会加载以XXX为名称的这个class文件进内存。并扫描其中有没有main方法。若有main方法JVM就会去调用这个main方法。JVM调用main方法,是不会创建对象的,没有对象怎么调用方法,这个方法只能被静态修饰。JVM通过类名调用的。

5.静态代码块

静态代码块,其实就在代码块前面加上了静态关键字,这个代码块就称为静态代码块。

优先于主方法执行,优先于构造代码块执行,不管创建多少对象,静态代码块只执行一次,可用于给静态变量赋值; 用来给类进行初始化。

静态代码块的应用场景类不需要创建对象,但需要初始化,这时可以将部分代码存储到静态代码块中

6.对象加载的流程总结

  1. 加载Demo.class文件进方法区,并进行空间分配。
  2. 如果有静态变量,先默认初始化,显示初始化。
  3. 如果有静态代码块,要执行,仅一次。
  4. 通过new在堆内存中开辟空间,并明确首地址。
  5. 对对象中的属性进行默认初始化。
  6. 调用对应的构造函数进行初始化。
  7. 构造函数内部。
    • 调用父类构造函数super();
    • 成员变量的显示初始化。
    • 构造代码块初始化。
    • 构造函数内容自定义内容初始化。
  8. 对象初始化完毕后,将地址赋值给d引用变量。

四、继承

1.继承的概念

在java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称作子类,现有类被称为父类,子类会自动拥有父类所有可继承的属性和方法。

继承的格式为:

[修饰符] class 子类名 extends 父类名{
  //程序核心代码
}

例如:

//将学生和工人的共性抽取到Person这个类中
class Person {
	String name;
	int age;
}
//学生通过extends继承Person,即Person为Student的父类
class Student extends Person{
	//学生类中的自己特有方法
	public void study(){
		System.out.println(name+"同学正在学习。。。。");
	}
}
//工人通过extends继承Person,即Person为工人的父类
class Worker extends Person{
	//工人类中的自己特有方法
	public void work(){
		System.out.println(name+"工人正在工作。。。。");
	}
}
//测试类
public  class Test{
	public static void main(String[] args) {
		Student s = new Student();
		s.name = "小明";
		s.study();
		
		Worker w = new Worker();
		w.name = "张三";
		w.work();
	}
}

继承的好处:

1、继承的出现提高了代码的复用性,提高软件开发效率。

2、继承的出现让类与类之间产生了关系,提供了多态的前提。

Java只支持单继承,不支持多继承。一个类只能有一个父类,不可以有多个父类。

Java支持多层继承(继承体系)

多继承虽然能使子类同时拥有多个父类的特征,但是其缺点也是很显著的,主要有两方面:

1.如果在一个子类继承的多个父类中拥有相同名字的实例变量,子类在引用该变量时将产生歧义,无法判断应该使用哪个父类的变量。

2.如果在一个子类继承的多个父类中拥有相同方法,子类中有没有覆盖该方法,那么调用该方法时将产生歧义,无法判断应该调用哪个父类的方法。

2.父类的重写及super关键字

在继承关系中,子类会自动继承父类中公共的方法,但有时在子类中需要对继承的方法进行修改,即对父类的方法进行重写。

当子类重写父类方法后,子类对象将无法直接访问父类被重写的方法。为了解决这个问题,在Java中专门提供了一个super关键字来访问父类的成员。

(1)使用super关键字调用父类的成员变量和成员方法

super.成员变量
super.成员方法([参数1,参数2,...])

例如:

class Animal{
    String name = "动物";
    void shout(){
        System.out.println("动物发出叫声");
    }
}
class Dog extends Animal{
    String name = "犬类";
    void shout(){
        super.shout();//访问父类的成员方法
    }
    void printName(){
        System.out.println("name="+super.name);//访问父类的成员变量
    }
}
public class Demo{
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
        dog.printName();
    }
}

(2)使用super关键字调用父类的构造方法

super([参数1,参数2...])

例如:

class Animal{
public Animal(String name){
    System.out.println("我是一只"+name);
}
}
class Dog extends Animal{
 public Dog(){
     super("柯基");//调用父类有参的构造方法
 }
}
public class Demo{
    public static void main(String[] args) {
        Dog dog = new Dog();
    }
}

3.重写的注意事项

重写需要注意的细节问题:

1、子类方法覆盖父类方法,必须要保证权限大于等于父类权限。

class Fu()
{	void show(){}
    public void method(){}
}
class Zi() extends Fu
{	public void show(){}  //编译运行没问题
    void method(){}      //编译错误
}

2、静态只能覆盖静态,或者被静态覆盖。

3、写法上稍微注意:必须一模一样:函数的返回值类型 函数名 参数列表都要一样。

总结:当一个类是另一个类中的一种时,可以通过继承,来扩展功能。如果父类具备的功能内容需要子类特殊定义时,使用重写

4.final关键字

final关键字可用于修饰类、变量和方法,它有“不可更改”或者“最终”的含义,因此被final修饰的类、变量和方法将具有以下特性:

(1)final修饰的类不能被继承

(2)final修饰的方法不能被子类重写

(3)final修饰的变量(成员变量和局部变量)是常量,只能赋值一次。

五、抽象类与接口

1.抽象类及其基本语法格式

分析事物时,发现了共性内容,就出现向上抽取。会有这样一种特殊情况,就是功能声明相同,但功能主体不同。那么这时也可以抽取,但只抽取方法声明,不抽取方法主体。那么此方法就是一个抽象方法。

[修饰符] abstract class 类名{
  [修饰符] absract 方法返回值类型 方法名([参数列表])//定义抽象方法 
}

2.抽象类的特点

1、抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。

2、抽象类不可以创建实例,原因:调用抽象方法没有意义

3、只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化。否则该子类还是一个抽象类。

包含抽象方法的类必须定义为抽象类,但抽象类中可以不包括任何抽象方法。另外,抽象类是不可以被实例化的,因为抽象类中有可能包含抽象方法,抽象方法是没有方法体的,不可以被调用。如果想调用抽象类中定义的抽象方法,需要创建一个子类,在子类中实现抽象类中的抽象方法。

抽象类和一般类的异同点:

相同

​ 1、它们都是用来描述事物的。

​ 2、它们之中都可以定义属性和行为。

不同

​ 1、一般类可以具体的描述事物。抽象类描述事物的信息不具体

​ 2、抽象类中可以多定义一个成员:抽象函数。

​ 3、一般类可以创建对象,而抽象类不能创建对象。

抽象类不可以和以下几个关键字共存:

1、final:fianl修饰的类是无法被继承的,而abstract修饰的类一定要有子类。 final修饰的方法无法被覆盖,但是abstract修饰的方法必须要被子类去实现的。

2、static:静态修饰的方法属于类的,它存在与静态区中,和对象就没关系了。而抽象方法没有方法体,使用类名调用它没有任何意义。

3、private:私有的方法子类是无法继承到的,也不存在覆盖,而abstractprivate一起使用修饰方法,abstract既要子类去实现这个方法,而private修饰子类根本无法得到父类这个方法。互相矛盾。

3.接口

接口是一种特殊的抽象类,它不能包含普通方法,其内部的所有方法都是抽象方法,它将抽象进行得更为彻底。

基本语法格式为:

[修饰符] interface 接口名 [extends 父接口1,父接口2...]{
  [public][static][final]常量类型 常量名 = 常量值;
  [public][abstract]方法返回值类型 方法名([参数列表]);
  [public] default 方法返回值类型 方法名([参数列表]){
    //默认方法的方法体
  }
   [public] static 方法返回值类型 方法名([参数列表]){
    //静态方法的方法体
  }
}

接口中成员的特点:

1、接口中可以定义变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量。

2、接口中可以定义方法,方法也有固定的修饰符,public abstract

3、接口中的成员都是公共的。

4、接口不可以创建对象。

5、子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类。

表示定义一个接口时,可以同时继承多个父接口.这也是为了解决类的单继承的限制。在接口内部可以定义多个常量和抽象方法,定义常量时必须进行初始化赋值,定义默认方法和静态方法,可以有方法体。

从接口定义的语法格式可以看出,接口中可以包含三类方法:抽象方法、默认方法和静态方法,其中静态方法可以通过“接口名.方法名”的形式来调用,而抽象方法和默认方法只能通过接口实现类的实例对象来调用。因此需要定义一个接口的实现类,该类通过implements关键字实现当前接口,并实现接口中的所有抽象方法。需要注意的是,一个类可以在继承另一个类的同时实现多个接口,并且多个接口之间需要使用英文逗号(,)分隔。

通过接口实现类实例化对象可以访问接口中的常量、接口实现方法以及默认方法,而接口中的静态方法则可以直接使用接口名调用。需要注意的是,接口的实现类,必须实现接口中的所有抽象方法,否则程序编译报错。

怎么解决多继承的弊端呢?

弊端:多继承时,当多个父类中有相同功能时,子类调用会产生不确定性

其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。

而在多实现里 因为接口中的功能都没有方法体,由子类来明确。

六、多态

1.多态概述

在java中,多态是指不同类的对象在调用同一个方法时所呈观出的多种不同行为。通常来说,在一个类中定义的属性和方法被其他类继承或重写后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法将呈现多种不同形态。通过多态,消除了类之间的耦合关系,大大提高了程序的可扩展性和可维护性。

Java的多态性是由类的继承、方法重写以及父类引用指向子类对象体现的。由于一个父类可以有多个子类,多个子类都可以重写父类方法,并且多个不同的子类对象也可以指向同一个父类;这样,程序只有在运行时才能知道具体代表的是哪个子类对象,这就体现了多态性。

2.对象的类型转换

将子类对象当作父类使用时不需要任何显式声明,需要注意的是,此时不能通过父类变量去调用子类的特有方法。

interface Animal{
    void shout();
}
class Cat implements Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
    public void catchMouth(){
        System.out.println("小猫抓老鼠");
    }
}
public class Demo{
    public static void main(String[] args) {
        Animal a1 = new Cat();
        Cat cat = (Cat) a1;
        cat.shout();
        cat.catchMouth();
    }
}

从上述代码可以看出,将本质为Cat类型的a1对象由animal类型向下转型为cat类型后,程序可以成功运行。需要注意的是,在进行对象向下类型转换时,必须转换为本质类型,否则转换时会出现错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值