面向对象


两大思想


编程界的两大思想:面向对象、面向过程。

面向过程,是一种以过程为中心的编程思想。分析出解决问题所需要的步骤,用方法依次把步骤实现出来再依次去执行步骤

面向对象,以一种事物为中心的编程思想。在分析和解决问题时把思维和重点转向现实中的客体中来,把构成事物分解成各个对象

例如看新闻,面向过程是打开报纸、查看新闻、放下报纸。面向对象是选择哪种方式看新闻、要看什么新闻、看什么语言的、看什么类型的(财经、体育、军事...)。面向过程是一种自顶向下的编程,面向对象是把事物抽象化

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

面向对象编程(OOP)

Java的编程语言是面向对象的,采用这种语言进行编程称为面向对象编程(Object-Oriented Programming, OOP)。

面向对象编程本质就是以类的方式组织代码,以对象的方式封装数据

1.面向对象三大主要特征

1.1.封装(Encapsulation)

在日常购物中,电商平台给予我们足不出户即可享受到购物的乐趣,当然,在购物中我们不用去关心电商平台其内部运作机制,我们只需要选购产品和付款即可。封装亦是如此,把内部的实现隐藏起来,然后只暴露必要的方法让外部调用。

1.2.继承(inheritance)

当然,选购产品和付款是电商平台最基本的功能,实际,电商平台还会在其基础上再多入新的功能,如直播选购、按喜好给用户推送产品等等一系列扩展的功能。继承就是后来者在保证原有功能不变的前提上,再在其体制上添加扩展的功能

1.3.多态(polymorphism)

当选购完产品,到给钱了,不同平台会用不同的结算方式,微信或支付宝结算,后台调用的接口也是不一样的,但后台调用什么接口我是不关心。多态就是可以用不同的方式完成同一个事件,微信、支付宝都是为了完成付款。方法重载本身就是一个多态性的体现。

2.抽象(abstract)

市面上的电商平台五花八门,但他们都有相同的共性,如选购产品和付款,有共性的一类事物可以作为一个抽象。在选购产品时,不管是什么平台,只要是同一类型的,都可以提供相同的功能

3.类与对象

类是一个蓝图,它只有属性的,是一类事物的描述。对象是一个具体,是类的实例化结果。

例如车,车是抽象的,有房车、越野车等都是具体的实例,这些实例就是对象

抽象来说:类与对象的关系就如模具和铸件的关系,类实例化的结果就是对象,而对对象的抽象就是类,类描述了一组有相同特性(属性)和相同行为的对象。

在java中,没有类就没有对象,然而类又是根据具体的功能需求,进行实际的分析,最终抽象出来的


方法

定义:修饰符 返回类型 方法名(参数列表)异常抛出类型{方法体}

1.定义

1.1.修饰符

public、static、abstract、final等等都是修饰符。可以有多个修饰符

1.2.返回类型

当方法执行完如果需要给调用者返回数据,需要给定返回数据的类型,没有返回的数据就必须写void,有标明返回类型的方法必须在结束处出现return语句

只有构造方法(构造器)不写任何返回类型也不写void

return的作用

1.终止一个函数的执行

2.向调用该函数的语句返回一个数据

3.如果return后面不数据,既返回空,目的是中断函数执行,返回调用函数处

break作用

1.在循环体内,强行结束循环的执行

2.在switch体内,跳出该switch语句体

1.3.方法名

函数的标识符,用于调用该函数,命名规则遵守java中标示符即可

1.4.参数列表

根据实际需求定义,可以有多个参数

1.5.异常抛出类型

方法在执行过程中,可能会出现一些异常的情况,当遇到这些情况,在方法上这些声明的异常既会被抛出,也可以声明抛出多个异常,使用逗号隔开即可。

public static int[] readIntArr(int[] arr)throws ArrayIndexOutOfBoundsException{}
public void readFile(String file) throws IOException,ClassNotFoundException{}

2.调用

2.1.非静态方法

没有使用static修饰符修饰的方法,调用这种方法的时候,是"一定"要使用对象的调用。因为非静态方法是属于对象的(非静态属性也是一样的)

public class Student{
    public int a = 10;
    public void say(){
    	System.out.println("hello!!");
    }
}

Student s = new Student();
s.say();
s.a;

2.2.静态方法

使用static修饰符修饰的方法,这种方法的时候,"可以"使用对象调用,也"可以"使用类来调用,但是推荐使用类进行调用。因为静态成员是属于类的(静态属性也是一样的),比非静态成员早加载

public class Student{
    public static int a = 10;
    public static void say(){
    	System.out.println("hello!!");
    }
}

Student.say();
Student.a();

2.3.类中方法之间的调用

同一个类中,都是非静态方法,可以用方法名去调用方法。如果都是静态方法,相互之间也可以直接调用。

如果两个方法中有一方是静态方法,另一方为非静态方法,那么静态方法不能调用非静态方法

public static num = 10;
public age = 30;
public void a(){b();}
public void b(){}

public static void c(){d();}
public static void d(){}

//静态方法不能调用非静态方法
//public static void e(){a();}
//非静态方法能调用静态方法
public void e(){c();}

public static f(){
    //System.out.println(age);会报错
}

在同一个类中,静态方法内不能直接访问到类中的非静态属性

3.方法的传参

3.1.形参和实参

public static void main(String[] args){
    int a = 1;
    int b = 3;
    add(a,b);
}
public static int add(int a, int b){
    return a+b;
}

函数add()上的参数a、b是add()函数的形参,main()函数体内的a、b是传递给add()函数的实参

3.2.值传递和引用传递

public class Student(){
    String name;
}
public class Test(){
    public static void main(String[] args){
        Student s = new Student();
        System.out.println(s.name);//null
        changeName(s);
        System.out.println(s.name);//Tom
        
        int a = 50;
        System.out.println(a);//50
        changeNum(a;
        System.out.println(a);//50
    } 
    public static void changeName(Student s){
        s.name = "Tom";
    }
    public static void changeNum(int num){
        num = 10;
    }
}

在程序中调用方法传递参数分两种情况,一个是值传递,一个是引用传递

值传递,传的是基本数据类型,是把变量本身存储的值赋值给形参。而引用传递,传的是引用数据类型,是把变量本身存的对象内存地址值赋值给形参。两者是有区别的,后者是可以根据内存地址改变对象里面的属性值

创建与初始化对象

在程序里,使用new关键字创建类的对象,格式:classType className = new classType()。在使用new的同时,也在内存中为该对象分配了空间,及对对象进行了默认的初始化和对类中构造器的调用

classType className = new classType()步骤解析

  1. 为对象分配内存空间,将对象的实例变量自动初始化默认值为0/false/null (实例变量的隐式赋值)
  2. 如果代码中实例变量有显式赋值,那么就将之前的默认值覆盖掉
  3. 调用构造器
  4. 把对象内存地址值赋值给变量(=号赋值操作)

1. 构造器

在进行创建对象的时候必须要调用构造器,也称为构造方法。

构造器的名字必须与类名相同,没有返回类型,也不能写void

1.1作用

  1. 在使用new创建对象的时候是要使用类的构造器

  2. 构造器中的代码执行后,可以给对象中的属性初始化赋值

    public class Student(){
        String name;
        int age;
        //无参构造器,也是默认构造器
        public Student(){
           	this.name = null;
            this.age = 0;
        }
        //有参构造器
        public Student(String name, int age){
            this.name = name;
            this.age = age;
        }
    }
    

如果我们没有手动编写构造器,那在编译器就会自动帮我们添加一个无参的构造器。在日常开发中还会定义有参构造器,方便在创建对象时给属性赋值。无参构造和有参构造可以同时存在,这属于构造器重载

构造器的修饰符可以定义为禁止对象被外部创建,只要把public改为private这样就不能在外部通过new来创建这个对象了

2.this关键字

2.1在类中的作用

public class Student{
    private String name;
	public void setName(String name){
    	this.name = name;
	}
	public void print(){
    	this.setName("Tom");
	}
}

在同一个类中,存在全局变量与局部变量使用同一标识符,那在类中调用该标识符会存在就近原则,如果调用的标识符靠近局部变量,那该标识符代表的就是局部变量,否则反之。想在靠近局部变量时调用全局变量的标识符可以用this,在这里this代表目前类的对象。this也可以用来调用本类的其他方法

2.2在类中的意义

this是代表所在类的对象,this()表示调用当前类构造器的代码,不会产生新的对象。但会产生递归

public class Student{
    String name;
 	//默认构造器
    public Student() {
        this();//这样属于递归构造函数调用
    }
	//有参构造器
    public Student(String name) {
        this.name = name;
    }
}

内存分析

Java虚拟机的内存大分为三个区域:栈,堆和方法区,其实细分是只有两个,因为方法区也是在堆里的。

栈(stack):

  1. 每个方法被调用都会创建一个栈帧,用以存储局部变量、操作数和方法出口
  2. 每创建一个线程都会为该线程创建一个栈,用以存储局部变量的基础数据类型和引用数据类型的引用
  3. 栈是线程私有的,数据不能在线程间共享
  4. 存储特点是:先进后出,后进先出
  5. 一个连续的内存空间!由系统自动分配,速度快!

堆 heap:

  1. 堆用于存储创建好的对象和数组,也就是new出来的对象
  2. JVM只有一个堆,被所有线程共享
  3. 一个不连续的内存空间,分配灵活,速度慢!

方法区

  1. 方法区是在堆里的,也是被所有线程共享
  2. 用于存储类、常量相关的信息
  3. 方法区中还存在一个静态方法区,里面是存放static修饰的方法,是和类一起加载的

Java内存

引用类型:

除基本类型外,其他的类型都是引用类型,引用就是对象的内存地址

类属性:

也叫成员变量,代表着类的属性,其作用范围是类的全局范围,定义格式:[modifiers] type name = [value],定义类属性可以不对其初始化,Java会使用默认的值对其初始化(整数:0,浮点数:0.0,char:u0000,boolean:false,引用类型:null)


封装

java三大特性之一封装,就是把内部数据“隐藏”起来,只暴露少量方法供外部使用,比如日常生活使用的电子产品,使用者只需要知道怎么去使用,不用去了解其内部构造。

高内聚,低耦合:内部数据不允许多部干涉,仅提供少量方法外部使用

对数据的隐藏,应禁止外部可直接访问对象内部数据,应通过接口间接访问

封装的步骤

  1. 使用private修饰需封装的成员变量

  2. 提供get/set方法供外部访问私有的内部成员

    public class Student{
    	private String name;
    	private int age;
    	private int ID;
        
        public Student(){}
        public Student(String name,int age,int id){
            this.name = name;
            this.age = age;
            this.ID = id;
        }
        //通过set方法让外部可以设置成员变量
    	public void setName(String name){this.name = name;}
        public void setAge(int age){this.age = age;}
        public void setID(int id){this.ID = id;}
        //通过get方法可以获取成员变量的值
        public String getName(){return this.name;}
        public int getAge(){return this.age;}
        public int getID(){return this.ID;}
    }
    

    在set方法中可以对数据合法性、完整性检测,让程序规避不合法的数据,如年龄应该在0~100之间,名字应该在32个字符内,学员id应该不超过20个数字等

作用和意义

  1. 成员私有化,保护了数据
  2. 提高程序的安全性、可维护性
  3. 统一用户的调用接口,便于调用者调用

由于成员私有化提供了访问接口,外部无法直接操作,保护了数据,提高程序的安全性,也对数据进行了合法的检测,确保了合法性;由于设罟了get/set方法使得代码更清晰,也提高了可维护性,方便外部调用

方法的重载

方法的重载可以提供方法调用的灵活性。

  1. 方法名必须相同
  2. 参数列表必须不同(参数的类型、个数、顺序的不同)
  3. 方法的返回值可以不同,也可以相同。
public void test(Strig str){}
public void test(int a){}

public void test(Strig str,double d){}
public void test(Strig str){}

public void test(Strig str,double d){}
public void test(double d,Strig str){}

相关