面向对象
两大思想
编程界的两大思想:面向对象、面向过程。
面向过程,是一种以过程为中心的编程思想。分析出解决问题所需要的步骤,用方法依次把步骤实现出来再依次去执行步骤
面向对象,以一种事物为中心的编程思想。在分析和解决问题时把思维和重点转向现实中的客体中来,把构成事物分解成各个对象
例如看新闻,面向过程是打开报纸、查看新闻、放下报纸。面向对象是选择哪种方式看新闻、要看什么新闻、看什么语言的、看什么类型的(财经、体育、军事...)。面向过程是一种自顶向下的编程,面向对象是把事物抽象化
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
面向对象编程(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()
步骤解析
- 为对象分配内存空间,将对象的实例变量自动初始化默认值为0/false/null (实例变量的隐式赋值)
- 如果代码中实例变量有显式赋值,那么就将之前的默认值覆盖掉
- 调用构造器
- 把对象内存地址值赋值给变量(=号赋值操作)
1. 构造器
在进行创建对象的时候必须要调用构造器,也称为构造方法。
构造器的名字必须与类名相同,没有返回类型,也不能写void
1.1作用
-
在使用new创建对象的时候是要使用类的构造器
-
构造器中的代码执行后,可以给对象中的属性初始化赋值
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):
- 每个方法被调用都会创建一个栈帧,用以存储局部变量、操作数和方法出口
- 每创建一个线程都会为该线程创建一个栈,用以存储局部变量的基础数据类型和引用数据类型的引用
- 栈是线程私有的,数据不能在线程间共享
- 存储特点是:先进后出,后进先出
- 一个连续的内存空间!由系统自动分配,速度快!
堆 heap:
- 堆用于存储创建好的对象和数组,也就是new出来的对象
- JVM只有一个堆,被所有线程共享
- 一个不连续的内存空间,分配灵活,速度慢!
方法区:
- 方法区是在堆里的,也是被所有线程共享
- 用于存储类、常量相关的信息
- 方法区中还存在一个静态方法区,里面是存放
static
修饰的方法,是和类一起加载的
引用类型:
除基本类型外,其他的类型都是引用类型,引用就是对象的内存地址
类属性:
也叫成员变量,代表着类的属性,其作用范围是类的全局范围,定义格式:[modifiers] type name = [value]
,定义类属性可以不对其初始化,Java会使用默认的值对其初始化(整数:0,浮点数:0.0,char:u0000,boolean:false,引用类型:null)
封装
java三大特性之一封装,就是把内部数据“隐藏”起来,只暴露少量方法供外部使用,比如日常生活使用的电子产品,使用者只需要知道怎么去使用,不用去了解其内部构造。
高内聚,低耦合:内部数据不允许多部干涉,仅提供少量方法外部使用
对数据的隐藏,应禁止外部可直接访问对象内部数据,应通过接口间接访问
封装的步骤
-
使用
private
修饰需封装的成员变量 -
提供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个数字等
作用和意义
- 成员私有化,保护了数据
- 提高程序的安全性、可维护性
- 统一用户的调用接口,便于调用者调用
由于成员私有化提供了访问接口,外部无法直接操作,保护了数据,提高程序的安全性,也对数据进行了合法的检测,确保了合法性;由于设罟了get/set方法使得代码更清晰,也提高了可维护性,方便外部调用
方法的重载
方法的重载可以提供方法调用的灵活性。
- 方法名必须相同
- 参数列表必须不同(参数的类型、个数、顺序的不同)
- 方法的返回值可以不同,也可以相同。
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){}