Java笔记:类与对象

第一章 类与对象

1.1 面向对象
面向对象三个主要特征:
  • 封装性:内部的操作对外部而言不可见,当内部的操作都不可直接使用的时候才是安全的;
  • 继承性:在已有结构的基础上,继续进行功能的扩充;
  • 多态性:是在继承性的基础上扩充而来的概念,指得是类型的转换处理
1.2 类与对象

类:类是对某一类实物的共性的抽象概念。

对象:对象描述的是一个具体的产物。

两者之间关系:对象是从类中实例化出来的例子。

1.3 对象内存分析

Java之中类属于引用数据类型,引用数据类型最大的困难之处在于要进行内存的管理,同时在操作的时候也会发生有内存关系的一个变化。

  • 堆内存:存储对象的具体信息(属性,方法等等)
  • 栈内存:存储分配出来的内存的地址(储存的是指向内存的指针)
// 类的定义
class Person{
	String name;
    int age;
    public void tell{
    	System.out.println("姓名: "+name);
        System.out.println("年龄: "+age);
    }
}

// 声明并实例化对象
Person p = new Person();
p.name = "张三";
p.age = 20;	
// 进行方法的调用
p.tell(); 	

//	 1.new 开辟了新的堆内存, new拥有开辟内存的最高级别
// 	 2.开辟的内存内容是要根据Person类来进行初始化的

注意:所有的对象在调用类中的属性或方法的时候必须要**实例化**完成后才可以执行。

1.4 对象引用分析

​ 1.4.1 引用传递分析

​ 类本身属于引用数据类型,既然是引用数据类型,那就牵扯到内存的引用传递,所谓的引用传递的本质:同一块堆内存空间,可以被不同的栈内存指向,也可以更换指向。(可以当成C指针)

例子:

public class Demo {
    public static void main(String[] args){
        Person p1  = new Person();
        p1.name = "zhangsan";
        p1.age = 20;
        p1.tell();
        System.out.println("-----------p2------------");
        Person p2 = p1; // 引用传递
        p2.age = 80;
        p2.tell();
        System.out.println("-----------p1------------");
        p1.tell();
    }
}

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

    }
}

Person p2 = p1;
p1 代表一个地址,比如0x0001
p2 = p1;
代表p2也等于 0x0001,指向堆空间中同一块内存
p2.age = 80;
修改了该堆空间中的数值,这时,p1本身数值没有改变仍是0x0001,也就是说,仍然指向着这块堆空间(已经被修改)所以再次调用p1.tell(); 后,输出 age也为80。

输出结果:

姓名 : zhangsan
年龄 : 20
--------------------p2--------------------------
姓名 : zhangsan
年龄 : 80
--------------------p1--------------------------
姓名 : zhangsan
年龄 : 80
1.5 引用与垃圾产生分析

**垃圾:**没有任何栈内存指向的堆内存空间,所有的垃圾将被GC(垃圾收集器,Garbage Collector)不定期进行回收并释放无用内存空间,但是如果垃圾过多,一定影响到GC的处理性能,从而降低整体的程序性能,所以在开发过程中,垃圾产生应该越少越好。

1.6.1 成员属性封装处理

在类之中的组成就是属性和方法,一般而言方法都是对外提供服务的,所以不需要封装,而属性需要较高的安全性,往往需要对其进行保护,这时候就需要采用封装性对属性进行保护。

属性封装方法:(类中所有属性都必须使用private封装!!!)

    1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
    public class Person {
        private String name;
        private int age;
    }
    

    这段代码中,将 nameage 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

    1. 【getter】、【setter】设置:对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对**私有属性(private)**的访问,一定要配合使用,否则外部还是能通过直接引用来修改属性,这就违背了使用setter和getter的意义

      例子:

      public class Demo {
        public static void main(String[] args) {
              Person p1 = new Person();
              p1.setName("张三");
              p1.setAge(19);
            p1.tell();
              //p1.name = "李四";
              /*
              这时这里已经无法直接对name属性进行修改了,
            会报错:'name' 在 'com.Person' 中具有 private 访问权限
              */
          }
      }
      
      
      class Person {
          /**
           * 思考:
           * 虽然我们使用了getter和setter进行类的封装,但是在调用方法上,依然可以使用.name这样的
           * 语句进行直接调用,有没有像python中 ._name这种形式使得在外部无法调用.name呢?
           *
           * 答案是有的,在Java中我们只要使用【private】权限修饰符修饰我们希望封装的对象属性即可。
           */
      
          private String name;
          private int age;
      
          /**
           * 使用setter和getter方法
           */
          public void setName(String name) {
              this.name = name;
          }
      
          public String getName() {
              return this.name;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public int getAge() {
              return this.age;
          }
      
          public void tell() {
              System.out.println("姓名" + this.name);
              System.out.println("年龄" + this.age);
          }
      
      }
      
1.6.2 this有三种用法:
  1. 当前类中的属性:this.属性名

使用this 调用当前类中属性

  • 代码第6行和第10行:this关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
  • 只要访问本类中的属性,一定加上this进行属性访问,养成好习惯
  1. 当前类中的方法(普通方法、构造方法):this.方法名()、this()
  • 构造方法调用(this()):使用关键字new实例化对象时才会调用构造方法
  • 普通方法调用(this.方法名称()): 利用重构和this() 来提高代码重用
  • 构造方法必须在实例化新对象时调用,因此this()的语句只允许放在【构造方法】的【首行】
  • 构造方法互相调用时,要注意别形成死循环。

例子:

public class Demo {
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println("--------【使用getter、setter】--------");
        System.out.println("--------【预期结果:张三  19】--------");
        p1.setName("张三");
        p1.setAge(19);
        p1.tell();

        System.out.println("--------【使用构造方法】--------");
        System.out.println("--------【预期结果: 李四  28 】--------");
        Person p2 = new Person("李四", 28);
        p2.tell();

        System.out.println("--------【使用this() 调用构造方法】--------");
        System.out.println("--------【预期结果: 打印两遍“我是一个构造函数~”  null  8  王五  80 】--------");
        // 构造方法只能在实例初始化的时候使用
        Person p3 = new Person(8);
        Person p4 = new Person("王五",80);

        p3.tell();
        p4.tell();
    }
}


class Person {
    /**
     * 思考:
     * 虽然我们使用了getter和setter进行类的封装,但是在调用方法上,依然可以使用.name这样的
     * 语句进行直接调用,有没有像python中 ._name这种方法使得在外部无法使用.name方法呢?
     * <p>
     * 答案是有的,在Java中我们只要使用private权限修饰符就可以了
     */

    private String name;
    private int age;

    /**
     * 使用setter和getter方法
     */
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

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

    public int getAge() {
        return this.age;
    }

    /**
     * 用this()方法,把构造函数和重构结合,减少代码重用
     */
//    public Person() {
//        System.out.println("我是一个构造函数~");
//    }
//
//    public Person(int age) {
//        System.out.println("我是一个构造函数~");
//        this.age = age;
//    }

    /**
     *  观察上方两个构造函数,发现有的部分是重复的代码,为了减少重复,
     *  可以使用 this() 来调用构造方法
     */
    
    //在java类中,如果不显示声明构造函数,JVM 会给该类一个默认的构造函数。一个类可以有多个构造函数。
    // 构造函数的主要作用:
    // 一是用来实例化该类。
    // 二是让该类实例化的时候执行哪些方法,初始化哪些属性
    // 当一个类声明了构造函数以后,JVM 是不会再给该类分配默认的构造函数。
    
    public Person() {
        System.out.println("我是一个构造函数~");
    }

    public Person(int age) {
        // 调用无参构造函数
        this();
        this.age = age;
    }

    /**
     * 构造函数: 可以解决参数过多,需要设置一大堆getter、setter的问题
     */
    public Person(String name, int age) {
        //调用单参构造函数
        this(age);
        this.name = name;
    }

    public void tell() {
        System.out.println("姓名" + this.name);
        System.out.println("年龄" + this.age);
    }

}

输出结果:

我是一个构造函数~
--------【使用getter、setter】--------
--------【预期结果:张三  19--------
姓名张三
年龄19
--------【使用构造方法】--------
--------【预期结果: 李四  28--------
我是一个构造函数~
姓名李四
年龄28
--------【使用this() 调用构造方法】--------
--------【预期结果: 打印两遍“我是一个构造函数~”  null  8  王五  80--------
我是一个构造函数~
我是一个构造函数~
姓名null
年龄8
姓名王五
年龄80
  1. 描述当前对象

.。。。。。。。。。。

1.7 构造方法与匿名对象

如果一个类中有较多的属性,那么用全部调用setter方法实在是太繁琐了,为了解决这个问题,Java中专门提供有构造方法,即,可以通过构造方法实现实例化对象中的属性初始化处理。

Java中构造方法的定义要求如下:

  • 方法名称必须与类名称保持一致;

  • 构造方法不允许设置任何返回值类型,也就是说,没有返回值定义。

  • 构造方法是在使用关键字new实例化对象的时候自动调用的。

    例子:

    public class Demo {
       public static void main(String[] args) {
           Person p1 = new Person("", 0);
           System.out.println("-------------【无封装👇】------------");
           p1.age = 20;
           p1.name = "zhangsan";
           p1.tell();
           System.out.println("-------------【构造函数👇】----------");
           //构造方法是在使用关键字new实例化对象的时候自动调用的。
           Person p2 = new Person("lisi", 19);
           p2.tell();
    
       }
    }
    
    class Person {
       String name;
       int age;
    
       public Person(String n, int a) {
         //方法名称必须与类名称保持一致;
         name = n;
         age = a;
    
       }
    
       public void tell() {
         System.out.println("姓名 : " + name);
         System.out.println("年龄 : " + age);
       }
    }
    

    注意:

    第20行处,构造方法不允许设置任何返回值类型,也就是说,没有返回值定义,也不用void!因为这里如果有类型定义,那么构造方法就与普通函数方法结构完全相同了,这样编译器会认为此方法是一个普通方法。
    两者的最大区别就在于:构造方法是在类对象实例化(new的时候)的时候调用的
    而普通方法是在类对象实例化完成之后调用的(new之后)

    输出结果:

    ----------------------【无封装👇】-------------------------
    姓名 : zhangsan
    年龄 : 20
    ----------------------【构造函数👇】-----------------------
    姓名 : lisi
    年龄 : 19
    
1.8 简单Java类(超重要)

所谓的简单Java类指的是可以描述某一类信息的程序类,例如:描述一个人、描述一本书,并且在这个类中并没有特别复杂的逻辑操作。只作为一种信息存储的媒介存在。

对于简单Java类而言,其核心开发结构如下:

- 类名称一定要有意义,可以明确的描述某一类事物。
  • 类之中所有属性都必须使用private进行封装,同时封装后的属性必须要提供有getter、setter方法。
  • 类之中可以提供无数多个构造方法,但是必须要保留有无参构造方法。建议写出一个无参构造在加上一个包含所有参数的构造方法,因为如果不显示的写出无参构造方法,而写出带参的构造方法,默认的无参构造方法就会被覆盖。
  • 类之中不能有任何输出语句,所有内容的获取必须返回。
  • 可以提供一个获取对象详细信息的方法,暂时将此方法名称定义为 getInfo() ;

例子:

/**
 * 现在假设有这样一个要求,定义一个雇员类,该类会包含雇员编号、姓名、职位、基本工资几个属性信息。
 *
 * @author Keendy
 */
public class SimpleClass {
    public static void main(String[] args) {
        System.out.println(new Emp(10124563, "Keendy", "Engineer", 3300.00).getInfo());
    }
}

class Emp {
    private int serial;
    private String name;
    private String position;
    private double salary;

    public void setSerial(int serial) {
        this.serial = serial;
    }

    public int getSerial() {
        return this.serial;
    }

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

    public String getName() {
        return this.name;
    }

    public void setPosition(String position) {
        this.position = position;
    }

    public String getPosition() {
        return this.position;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public double getSalary() {
        return this.salary;
    }

    /**
     * 无参构造
     */
    public Emp() {
    }

    /**
     * 有参构造
     */

    public Emp(int serial, String name, String position, double salary) {
        this.serial = serial;
        this.name = name;
        this.position = position;
        this.salary = salary;
    }


    public String getInfo() {
        return "编号:" + this.serial + '\n' + "姓名:" + this.name + "\n" + "职位:" + this.position + "\n" + "薪水:" + this.salary;
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值