JavaSE——类与对象(1)

1.面向对象编程简述

面向过程编程缺少了可重用设计性

1.1.面向对象三大特征

  • 封装性:所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。简而言之,内部操作对外部而言不可见(保护性
  • 继承性:继承是一种能力,指的是他可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展
  • 多态性:所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口

1.2.名词扩展

  • OOA:面向对象分析
  • OOD:面向对象设计
  • OOP:面向对象编程

面向对象最大的特征就是可以进行生活的抽象

2.类与对象的定义和使用

2.1.类与对象的概念

  • :共性的概念
  • 对象:一个具体的可使用的事物

编程中首先产生类(类是生产对象的蓝图),而后再产生对象。对象的所有行为一定在类中进行了完整的定义

类的组成:

  • 方法:操作行为
  • 属性:操作数据,描述对象的具体特点

2.2.类与对象的定义和使用

定义一个类的语法如下:

class 类名称{
    属性1;
    属性2;
    ...
    属性n;

    方法1(){}
    方法2(){}
    ...
    方法n(){}
}

如上是一个类的完整定义,此时类中的方法要由类的对象去调用。Person类定义示例如下:

class Person{
    public String name;
    public int age;
    
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    public String getPersonInfo(){
        return "姓名:" + name + "、年龄:" + age;
    }
}

有了类,现在我们可以定义对象了,生产对象的语法如下:

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

以Person类为例生产一个Person对象:

Person per1 = new Person("邹大",18);

通过对象调用实例变量与实例方法,代码如下:

class Person{
    public String name;
    public int age;

    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getPersonInfo(){
        return "姓名:" + name + "、年龄:" + age;
    }
}

public class Test{
    public static void main(String[] args) {
        Person per1 = new Person("邹大",18);
        System.out.println(per1.name);
        System.out.println(per1.age);
        System.out.println(per1.getPersonInfo());
    }
}
//邹大
//18
//姓名:邹大、年龄:18

!!!!注:只要出现了关键字new,就开辟了新的内存,而Java中常说的性能调优其实就是调整内存

2.3.对象内存分析

我们可以简单将Java中的内存区域分为栈内存堆内存两块区域(实际Java内存区域的划分远比这个复杂)

  • 栈内存(虚拟机局部变量表):存放的是局部变量(包含编译期可知的各种基本数据类型对象引用——即堆内存的地址,可以简单理解为对象名称),Java栈是与线程对应起来的,每当创建一个线程,JVM就会为这个线程创建一个对应的Java栈
  • 堆内存:保存的是真正的数据,即对象的属性信息

通过如下代码分析两部分内存:

class Person{
    String name;
    int age;
}
public class Test {
    public static void main(String[] args) {
        Person per = new Person();
        per.name = "tim";
        per.age = 18;
    }
}

main方法中的第一行代码:

Person per = new Person();

如上出现了关键字new,表名在堆上分配了内存并且产生了Person类的对象per引用这部分内容,内存图如下:
在这里插入图片描述
接下来两句代码:

per.name = "tim";
per.age = 18;

通过per引用设置堆中属性值,内存图如下:
在这里插入图片描述


       对象(引用数据类型)必须在实例化后调用,否则就会产生NullPointerException(运行时错误),编译时不会出错。“NullPointerException”在开发中会一直存在,只有引用类型(数组、类、接口)才会产生此类异常。一旦出现此类异常,就要根据出错位置查看引用数据类型变量是否已经初始化

2.4.引用传递分析

引用传递的本质一块堆内存可以被多个栈内存所指向

Person per1 = new Person();
Person per2 = new Person();
per2 = per1;

前两句代码内存图如下:
在这里插入图片描述
那么当per2 = per1执行后,内存图如下:
在这里插入图片描述
总结:

  • 垃圾内存:指没有任何栈内存指向的堆内存空间
  • 所有的垃圾空间都会不定期GC(垃圾回收)GC会影响性能,所以开发中要控制好对象产生数量(无用的对象尽量少产生)。

3.private实现封装处理以及构造方法(匿名对象)

3.1.private实现封装处理

封装:封装是一个非常复杂的概念,使用private关键字实现的封装处理只是封装的第一步,更多的了解要建立在继承和多态学习上。
无封装程序示例:

class Person{
    String name;
    int age;
    public void getPersonInfo(){
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}

public class Test {
    public static void main(String[] args) {
        Person per = new Person();
        per.name = "邹大";
        per.age = 18;
        per.getPersonInfo();
    }
}
//姓名:邹大、年龄:18

此时,要回避此类问题,让内部操作对外部不可见(对象不能直接操作属性),可以使用private进行封装,用private封装属性示例如下:

private String name;
private int age;

此时使用了private对属性进行封装,要访问私有属性,按照Java设计原理必须提供一下两种法:

  • setter方法:主要用于对属性值得设置和修改
  • getter方法:主要用于属性内容的取得

使用private封装属性示例如下:

class Person{
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
    public void getPersonInfo(){
        System.out.println("姓名:" + name + "、年龄:" + age);
    }
}

public class Test {
    public static void main(String[] args) {
        Person per = new Person();
        per.setName("tim");
        per.setAge(18);
        per.getPersonInfo();
    }
}
//姓名:tim、年龄:18

通过如上代码我们可以发现,private实现封装的最大特征在于:只允许本类访问而不允许外部类访问


类的设计原则:

  • 以后编写类时,类中的所有属性必须使用private进行封装
  • 属性若要被外部访问,必须定义getter和setter方法

3.2.构造方法与匿名对象

3.2.1.构造方法简述

根据如下对象产生过程展开分析:

(1)类名称 (2)对象名称 = (3)new (4)类名称();
  • (1)任何对象都应该有其对应的类,因为类是产生对象的蓝图
  • (2)对象名称是一个唯一的标记,引用一块堆内存
  • (3)new表示开辟新的堆内存空间
  • (4)是构造方法

通过如上分析可知,所谓构造方法就是使用关键字new实例化新对象时来调用的操作方法。对于构造方法的定义要遵循以下原则:

  • 方法名称必须与类名相同
  • 构造方法没有返回值类型声明
  • 每个类至少存在一个构造方法(没有明确定义,系统会默认生成一个无参构造
  • 若类中定义了构造方法,则默认的无参构造就不会再生成

问题:构造方法无返回值,为什么没有void声明?
解释上述问题要先明确类中的组成:属性、构造方法、普通方法

  • 属性是在对象开辟堆内存时开辟空间
  • 构造方法是在使用new后调用
  • 普通方法是在空间开辟了、构造方法执行之后可以被多次调用的
public Person(){}//无参构造方法

       编译器是根据程序结构来区分普通方法和构造方法的,所以在构造方法前没有返回值类型声明。

使用构造方法设置对象属性示例如下:

class Person{
    private String name;
    private int age;
    public Person(String n,int a){
        setName(n);
        setAge(a);
    }
    public void setName(String n){
        name = n;
    }

    public void setAge(int a) {
        if(a > 0 && a <= 150){
            age = a;
        }else{
            age = 0;
        }
    }
    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
    public void getPersonInfo(){
        System.out.println("姓名:" + getName() + "、年龄:" + getAge());
    }
}

public class Test{
    public static void main(String[] args) {
        Person per = new Person("Tim",18);
        Person per1 = new Person("Tim",200);
        per.getPersonInfo();
        per1.getPersonInfo();
    }
}
//姓名:Tim、年龄:18
//姓名:Tim、年龄:0     

构造方法的调用和对象内存分配几乎是同时完成的,因此我们可以利用构造方法来为为类中的属性进行初始化操作(可以避免多次setter操作

3.2.2.构造方法

构造方法重载:参数类型或个数不同
构造参数重载示例:

public Person(){
    System.out.println("===无参构造===");
}
public Person(String n){
    name = n ;
    System.out.println("===有参构造===");
}

建议:

  • 如果有若干构造方法,一般参照参数个数升序或降序排列
  • 在进行类定义时:(1)定义属性——(2)定义构造方法——(3)定义普通方法

匿名对象示例:

new Person("张三"18).getPersonInfo();

由于匿名对象不会有任何的栈空间指向,所以使用一次之后就会成为垃圾空间。

4.this关键字

this关键字主要有三个方面的用途:

  • this调用本类属性
  • this调用本类方法
  • this表示当前对象

4.1.this调用本类属性

class Person{
    private String name;
    private int age;

    public Person(String name,int age){
        name = name;
        age = age;
    }

    public String getPersonInfo(){
        return "姓名:" + name + "、年龄:" + age;
    }
}

public class Test{
    public static void main(String[] args) {
        Person per = new Person("Tim",18);
        System.out.println(per.getPersonInfo());
    }
}
//姓名:null、年龄:0

通过如上代码我们可以发现,当参数与类中属性名相同时,类中属性无法被正常赋值。因此我们需要加上this关键字,这样便可以正确给对象属性赋值,如下:

class Person{
    private String name;
    private int age;

    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getPersonInfo(){
        return "姓名:" + name + "、年龄:" + age;
    }
}

public class Test{
    public static void main(String[] args) {
        Person per = new Person("Tim",18);
        System.out.println(per.getPersonInfo());
    }
}
//姓名:Tim、年龄:18

只要在类中方法访问类中属性一定要加this关键字

4.2.this调用本类方法

this调用本类方法有如下两种情况:

  • 调用普通方法
this.方法名称(参数)
  • 调用构造方法
this(参数)

this调用普通方法示例:

class Person{
    private String name;
    private int age;

    public Person(String name,int age){
        this.name = name;
        this.age = age;
        this.print();//调用普通方法
    }

    public void print(){
        System.out.println("**********");
    }

    public String getPersonInfo(){
        return "姓名:" + name + "、年龄:" + age;
    }
}

public class Test{
    public static void main(String[] args) {
        Person per = new Person("Tim",22);
        System.out.println(per.getPersonInfo());
    }
}
//**********
//姓名:Tim、年龄:22

虽然调用本类方法不加this也可以正常调用,但是一般建议加上,加上this的目的是可以区分方法的定义来源(在继承中有用)
观察构造方法中存在的问题:

class Person{
    private String name;
    private int age;
    public Person(){
        System.out.println("***产生一个新的Person对象***");
    }
    public Person(String name){
        System.out.println("***产生一个新的Person对象***");
        this.name = name;
    }
    public Person(String name,int age){
        System.out.println("***产生一个新的Person对象***");
        this.name = name;
        this.age = age;
    }
    public String getPersonInfo(){
        return "姓名:" + name + ",年龄:"+age;
    }
}
 public class Test{
     public static void main(String[] args) {
         Person per1 = new Person();
         Person per2 = new Person("张三");
         Person per3 = new Person("李四",18);
         System.out.println(per1.getPersonInfo());
         System.out.println(per2.getPersonInfo());
         System.out.println(per3.getPersonInfo());
     }
 }
 //姓名:null,年龄:0
//姓名:张三,年龄:0
//姓名:李四,年龄:18

上述代码中存在问题:有大量重复代码
Java中支持构造方法的互相调用(this),所以修改如上代码:

class Person{
    private String name;
    private int age;
    public Person(){
        System.out.println("***产生一个新的Person对象***");
    }
    public Person(String name){
        this();//调用本类无参构造
        this.name = name;
    }
    public Person(String name,int age){
        this(name);//调用本类有参构造
        this.age = age;
    }
    public String getPersonInfo(){
        return "姓名:" + name + ",年龄:"+age;
    }
}
public class Test{
    public static void main(String[] args) {
        Person per1 = new Person();
        Person per2 = new Person("张三");
        Person per3 = new Person("李四",18);
        System.out.println(per1.getPersonInfo());
        System.out.println(per2.getPersonInfo());
        System.out.println(per3.getPersonInfo());
    }
}

!!!!!!this调用构造方法时要注意

  • this调用构造方法的语句必须在构造方法首行
  • 使用this调用构造方法时,请留有出口(换句话说就是调用的构造方法是已经实现好的、正确的,一定避免出现互相调用,那样没有出路就会出错),如下代码就没有出路
public Person(String name.int age){
    this();
}
public Person(){
    this("s",18);
}

4.3.使用this表示当前对象

this表示当前对象示例代码如下:

class Person{
    public void print(){
        System.out.println("[PRINT]方法" + this);
    }
}

public class Test{
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println("[MAIN]方法" + p1);
        p1.print();
        System.out.println("______________");
        Person p2 = new Person();
        System.out.println("[MAIN]方法" + p2);
        p2.print();
    }
}
//[MAIN]方法Person@1b6d3586
//[PRINT]方法Person@1b6d3586
//______________
//[MAIN]方法Person@4554617c
//[PRINT]方法Person@4554617c

只要对象调用了本类中的方法,方法中的这个this就表示当前执行的对象

5.static关键字

static可以修饰属性和方法

5.1.static属性(类属性)

class Person{
    String country = "中国";
    String name;
    int age;

    public void getPersonInfo(){
        System.out.println("姓名:" + this.name + "、年龄:" + this.age + "、国家:" + this.country);
    }
}

public class Test{
    public static void main(String[] args) {
        Person per1 = new Person();
        per1.name = "邹大";
        per1.age = 29;
        Person per2 = new Person();
        per2.name = "邹二";
        per2.age = 18;
        per1.getPersonInfo();
        per2.getPersonInfo();
    }
}
//姓名:邹大、年龄:29、国家:中国
//姓名:邹二、年龄:18、国家:中国

上述代码的内存分析图如下:
在这里插入图片描述
传统属性所具备特征:保存在堆内存中,且每个对象独享属性

描述共享属性:在属性前添加static关键字即可

static属性:又称类属性,保存在全局数据区的内存之中,所有对象都可以进行该数据区的访问

修改上述代码:

static String country = "中国";

修改后代码内存分析图如下:
在这里插入图片描述
结论:

  • 访问static属性(类属性)应使用类名.属性名,如下:
System.out.println(Person.country);
  • 所有的非static属性(实例变量)必须在对象实例化后使用,而static属性不受对象实例化控制
  • 修改static属性,所有对象都同步此属性值

定义时如何选择实例变量和类属性:

  • 在定义类时,99%的情况都不会考虑static属性,以非static属性(实例变量)为主
  • 如果需要描述共享属性概念或者不受对象实例化控制,就使用static

5.2.static方法(类方法)

使用static定义方法,通过类名称直接访问,示例如下:

class Person {
    static String country;
    String name;
    int age;

    public Person(String name,int age){
        this.name = name ;
        this.age = age ;
    }

    public static void setCountry(String c){
        country = c ;
    }

    public void getPersonInfo() {
        System.out.println("姓名:" + this.name + "、年龄:" + this.age + "、国家:" + this.country);
    }
}

public class Test{
    public static void main(String[] args) {
        Person.setCountry("中国");
        Person per = new Person("张三",19);
        per.getPersonInfo();
    }
}
//姓名:张三、年龄:19、国家:中国

关于static方法:

  • 所有的静态方法不允许调用普通属性或方法:普通属性或方法依赖对象而生,无对象产生是无法访问的,静态方法只能访问类中静态属性
  • 所有普通方法允许访问类中静态方法或静态属性也可以访问类中普通属性

使用static定义方法只有目的:使某些方法不受到类的控制,即可以在没有实例化对象的时候执行(广泛存在于工具类中),如:

Arrays.copyOf();
Array.sort();
System.arraycopy();

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值