面向对象
简介
面向过程的操作是以程序的基本功能实现为主,开发的过程中只是针对问题本身的实现,并没有很好的模块化的设计,所以在进行代码维护的时候较为麻烦。而面向对象,采用的更多的是进行子模块化的设计,每一个模块都需要单独存在,并且可以被重复利用。总的来说面向对象关注的是对象本身,既模块化的设计,
特征主要有封装,继承,多态
封装
继承
多态
类与对象
基本概念
类和对象的关系:类实际上是表示一个客观世界中某类群体的一些基本特征抽象,属于抽象的概念集合,对象就是表示一个个具体的事物。
例如,在现实生活中,人就可以表示为一个类,因为人本身属于一种广义的概念,并不是一个具体个体描述。而某一个具体的人,如张三同学,就可以称为对象,
定义语法
类:类是由属性和方法组成的。属性就是一个变量,方法是一些操作的行为
说明:
- 类:类是由属性和方法组成的
- 常见名称:
属性=成员变量=filed=域,字段
方法=成员方法=函数=method
创建类的对象=类的实例化=实例化类
例如:
class Person{//定义一个Person类
String name;//人名
int age;//年龄
boolean isMale;//是否为男性
public void eat() {//调用对象的行为
System.out.println("我在吃饭");
}
}
//类的使用
public class ObjTest {
public static void main(String[] args) {
//创建对象
Person person = new Person();
//调用对象的结构
person.name="Java";
person.age=10;
person.isMale=true;
System.out.println(person);
person.eat();
}
- 内存解析:数据类型的执行分析就必须结合内存操作来看。下面给出两块内存空间的概念。
- 堆内存(heap):保存每一个对象的属性内容,堆内存需要用关键字new才可以开辟,如果一个对象没有对应的堆内存指向,将无法使用;
- 栈内存(stack):保存的是一块堆内存的地址数值,可以把它想象成一个int型变量(每一个int 型变量只能存放一个数值),所以每一块栈内存只能够保留一块堆内存地址。
个人理解:下面换个角度来说明这两块内存空间的作用。 掌握以上内存分析方法对于程序理解与概念应用是非常重要的。
- 堆内存:保存对象的真正数据,都是每一个对象的属性内容;
- 栈内存:保存的是一块堆内存的空间地址,但是为了方便理解,可以简单地将栈内存中保存的数据理解为对象的名称(Person person),就假设保存的是“person”对象名称
上面的例子的内存解析
说明:
在所有的引用分析里面,最关键的还是关键字“new”。一定要注意的是,每一次使用关键字new 都一定会开辟新的堆内存空间,所以如果在代码里面声明两个对象,并且使用了关键字new为两个对象 分别进行对象的实例化操作,那么一定是各自占有各自的堆内存空间,并且不会互相影响。
属性(成员变量)和局部变量
相同点:
- 定义格式:数据类型 变量名=变量值;
- 先声明后使用
- 量都有其对应的作用域
不同点:
- 声明位置不同:属性:定义在{},局部变量:声明在方法内,形参,代码块,
- 修饰符不同:属性:使用属性修饰符,包括public,protected,default,private;局部变量:使用不了
- 默认初始化值:属性:类的属性:根据类的属性都有初始化值;局部变量:无初始化值。我们在调用局部变量,必须显性赋值。注意:形参在调用是,直接赋值即可
- 内存加载位置不同:属性:堆(非static); 局部变量:栈
方法的重载
方法重载
在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同。体现了多态性
public class OverLoadTest {
public static void main(String[] args) {
OverLoadTest overLoadTest=new OverLoadTest();
overLoadTest.getSum(1,2);
overLoadTest.getSum(1.2,1.5);
overLoadTest.getSum(1,1.5);
}
//下面3个方法体现了重载
public void getSum(int i,int j){
System.out.println(i + j);
}
public void getSum(double i,double j){
System.out.println(i + j);
}
public void getSum(int i,double j){
System.out.println(i + j);
}
}
说明:
- 是否重载跟方法的权限修饰符,返回值类型,形参,方法体没有关系。
- 调用方法时候,如何确定某个指导方法:方法名–>参数列表
可变形参
- jdk1.5新增的内容.
- 使用格式: 格式:数据类型…变量名
public static void main(String[] args) {
MethodArgsTest methodArgsTest = new MethodArgsTest();
methodArgsTest.show(123);
methodArgsTest.show("hello");
methodArgsTest.show("hello","world");
}
//重载
public void show(int i){
System.out.println("i="+i);
}
public void show(String str){
System.out.println("str="+str);
}
//调用可变个数的形参方法
public void show(String... str){
Arrays.stream(str).forEach(System.out::println);
}
说明:
- 当调用可变个数的形参方法时,数量可以是1,2,3甚至是多个,与本类的其他同名的方法构成重载
- jdk1.5之前使用String[]传入,后使用String…,两者不构成重载
- 必须声明在形参列表末尾,可变形参最多只有一个
public void show(String...str,int j){}//错误,Vararg parameter must be the last in the list
方法引用传递
引用传递是整个Java的精髓所在,而引用传递核心意义是:同一块堆内存空间可以被不同的栈内存所指向,不同栈内存可以对同一堆内存进行内容的修改。下面例子
public static void main(String[] args) {
int m=10;
int n=20;
ValueTransferTest1 valueTransferTest1 = new ValueTransferTest1();
valueTransferTest1.swap(10,20);
System.out.println("m="+m+",n="+n);
}
public void swap(int m,int n){
int temp=m;
m=n;
n=temp;
}
m=10,n=20
内存解析:如下
形参与实参: 形参方法声明的参数,实参方法调用是实际传给形参的参数值
如果是基本数据类型:实参–>形参,数据值
如果是英语数据类型:实参–>形参,地址值
递归
自己调用自己本身,必须含有终止条件,递归体
举例:菲波那次数列。
面向对象特征:封装
封装
封装是面向对象的方法所应遵循的一个重要原则。它有两个含义:一层含义是指把对象的属性和行为看成一个密不可分的整体,将这两者“封装”在一个不可分割的独立单位(即对象)中;另一层含义指信息隐蔽,把不需要让外界知道的信息隐藏起来。隐藏对象内部的复杂性,只提供公开的接口,便于外部调用。
public class BookTest {
public static void main(String[] args) {
Book book = new Book();
book.setTitle("Java开发");
book.setPrice(-99.0);
book.getInfo();
}
}
class Book{
/*书的名字*/
private String title;
/*书的价格*/
private Double price;
/*此方法将由对象调用*/
public void getInfo() {
System.out.println("图书名称:" + title + ",价格:" + price);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Double getPrice() {
return price;
}
//数据验证
public void setPrice(Double price) {
this.price = price>0?price:null;
}
}
上面体现了使用private封装属性。
封装性的体现
- 属性的私有化,同时提供公共的方法来获取(getXxx)和设置(setXxx)
- 不对外暴露的私有方法
- 单例模式
权限修饰符
用来修饰成员的访问权限,从大到小,pubic,protected,缺省(default)
说明:
- 四种都能修饰类的内部结构,包括方法,属性,构造器,内部类
- 修饰类只能用public 缺省
构造器
构造器(构造方法):在实例化类前,可能在对象实例化时为其进行一些初始化的准备操作,这个时候就需要构造方法的支持。构造方法本身是一种特殊的方法,它只在新对象实例化的时候调用,其定义原则是:方法名称与类名称相同,没有返回值类型声明,同时构造方法也可以进行重载。 相当于在new 某类之前的一个初始化方法
作用:
- 创建对象
- 初始化信息
格式:
权限修饰符 泪目(形参列表){}//注意格式不能用void修饰
例子:
public static void main(String[] args) {
//空参构造
Uesr uesr = new Uesr();
}
class Uesr{
//成员变量(属性)
String name;
public Uesr(){
System.out.println("Uesr.User");
}
}
Uesr.User
可以看见当new User()的时候,调用了空参构造器的方法,本程序在User类中定义了一个构造方法,可以发现构造方法的名称与User类名称相同,没有返回值声明,并且构造方法只有在使用关键字new实例化对象时才会被调用一次。
说明
一旦显式定义了类的构造器之后,系统不在提过默认的空参构造器。
提问:构造方法与普通方法的区别?
构造方法与普通方法的调用时机不同。
首先在一个类中可以定义构造方法与普通方法两种类型的方法,但是这两种方法在调 用时有明显的区别:
- 构造方法是在实例化新对象(new)的时候只调用一次;
- 普通方法是在实例化对象产生之后,通过“对象.方法”调用多次。
如果在构造方法上使用了void,其定义的结构与普通方法就完全一样,而程序的编译是依靠定义结构来解析的,所以不能有返回值声明。
this关键字
this修饰属性,方法,构造器;
当getXxx()和setXxx()形参和实参名相同时候,可以使用this表示当前对象的属性。
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//name = name;编译器识别不了name是当前类的属性还是方法的形参;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
//age = age;同理
}
//构造器中也可以使用this
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
}
this只能调用其他构造器,格式为this(形参列表)
public class Person {
private String name;
private Integer age;
public Person(){
System.out.println("Person.Person");
}
public Person(String name,Integer age){
//调用无参构造器
this();
this.name=name;
this.age=age;
}
}
public class PersonTest {
public static void main(String[] args) {
Person java = new Person("JAVA",0);
}
}
Person.Person
注意:this不能掉用自己,内部最多只能声明一次this(形参)