Java学习之路之week3day3

向着Java程序员的目标前进!

day12

面向对象—续2

构造方法

构造方法是一种特殊的方法,作用是给对象的数据进行初始化。

构造方法的特殊性(与一般方法比较):

  1. 构造方法的方法名与类名相同
  2. 没有具体的返回值类型,连void都没有
  3. 没有具体的返回值

与一般方法的联系:可以重载

构造方法使用的注意事项:

  1. 在构造方法中,没有返回值类型,连void都没有。所以不要随便定义构造方法。

  2. 一个类如果不是测试类,在没有提供任何构造方法的情况下,系统会默认给我们提供无参构造方法。但是我们在学习了构造方法后,永远要记得定义无参构造方法,因为继承中会用到!

  3. 如果我们在一个类中给出了有参构造方法,那么系统就不会再提供无参构造方法。当你使用无参构造方法创建对象时,系统就会报错。(这也是永远要记得给出无参构造方法的原因之一)

补充:到现在为止,给成员变量数据初始化由两种方式:

  1. 通过无参构造方法+setXXX()赋值,通过getXXX()获取值
  2. 通过有参构造方法直接赋值,getXXX()获取值
构造方法的使用举例
/**
 * 需求:创建学生类,属性包括姓名、年龄、性别、身高、爱好;
 * 方法包括学习、抽烟
 * 创建完毕后进行测试
 */

class Student {
    private String name ; //姓名
    private int age ; //年龄
    private String gender  ;//性别
    private String hobby ; //爱好
    private int high ;//身高

    //提供无参构造方法
    //快捷键:alt+ins(+fn:自己电脑可能需要一直按上)
    // --->constructor--->select none 无参构造
    public Student() {
    }
    //有参构造方法:自己先手写完属性----再使用快捷完成
    //alt+ins+(fn:自己电脑可能需要一直按上)
    // --->constructor--->全选所有属性赋值
    public Student(String name, int age, String gender, String hobby, int high) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.hobby = hobby;
        this.high = high;
    }
    //提供对外的公共的SetXXX()/getXXX()方法
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public int getHigh() {
        return high;
    }

    public void setHigh(int high) {
        this.high = high;
    }
    //其他的成员方法
    //学习,抽烟...
    public void study(String className){
        System.out.println("正在学习"+className);
    }

    //返回String
    public String smoke(String brandSmoke){//烟的牌子
        return "学生抽"+brandSmoke ;
    }
}

class StudentTest {
    public static void main(String[] args) {
        //方式1:无参构造方法+setXXX()/getXXX()
        Student student = new Student() ;//系统默认初始化
        //显式初始化
        student.setName("张三丰") ;
        student.setAge(50) ;
        student.setGender("男") ;
        student.setHigh(180) ;
        student.setHobby("太极") ;

        System.out.println("这个学生的信息如下:");
        System.out.println("姓名\t年龄\t性别\t身高\t爱好");
        System.out.println(student.getName()+"\t"+student.getAge()+"\t\t"+student.getGender()+"\t\t"+student.getHigh()+"\t\t"+student.getHobby());
        //调用其他方法
        student.study("Java");
        String str = student.smoke("中华");
        System.out.println(str);

        System.out.println("-----------------------------------") ;

        //方式2:有参构造方法+getXXX()
        //  public Student(String name, int age, String gender, String hobby, int high) {
        Student s2 = new Student("张亮",20,"男","健身",185)  ;//必须要符合构造方法的参数的先后顺序

        System.out.println("这个学生的信息如下:");
        System.out.println("姓名\t年龄\t性别\t身高\t爱好");
        System.out.println(s2.getName()+"\t"+s2.getAge()+"\t\t"
                +s2.getGender()+"\t\t"+s2.getHigh()+"\t\t"+s2.getHobby());
        //调用其他方法
        s2.study("Java");
        String result = s2.smoke("云烟");
        System.out.println(result);
    }
}

面试题:创建对象时内存中的过程

题目:有一个学生类,创建学生类对象 Student s = new Student(); 完成了哪些事情?

解析:在面试时不要言简意赅的说“创建了一个对象”,而是应该从你擅长的角度娓娓道来。面试官看的不仅是你的技术能力,而且还看你的表达能力。

解答:

创建对象的整个过程:

  1. 加载学生类Student.class字节码文件
  2. 在栈内存中开辟空间
  3. 在内存中new Student(); 申请堆内存空间
  4. 申请的同时给当前类中的成员变量进行系统默认的初始化(无参构造方法)
  5. 在堆内存中产生一个堆内存空间地址值
  6. 将栈内存空间地址值赋值给栈内存中的变量s
  7. 栈内存中的变量s指向堆内存地址(类似C语言的指针)
  8. 当对象创建完毕,GC垃圾回收器在空闲时会回收这些没有更多引用的对象

补充:

在内存中,每一个类都有自己的“签名信息”(版本ID,唯一标识符)。这类似于每个文件的属性中都有文件类型、大小等信息,我们把这些信息叫做元数据。

static关键字

从一个例子说起
/**
 * 需求:定义一个Person类,里面有姓名、年龄和国籍的属性
 * 提供一个成员方法show方法,显示当前人的成员信息 (为了方便,暂时先不加入私有修饰属性)
 * 创建PersonTest类并分别创建一些人,进行测试,
 *
 */

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

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

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

    public void show(){
        System.out.println("当前人的姓名是"+name+",年龄是:"+age+",所在的国籍是:"+country);
    }
}

class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person("西施",18,"中国");
        p1.show() ;
        System.out.println("------------------------------------");
        Person p2 = new Person("杨贵妃",28,"中国");
        p2.show();
        System.out.println("------------------------------------");
        Person p3 = new Person("王昭君",25,"中国");
        p3.show();
        System.out.println("------------------------------------");
        Person p4 = new Person("貂蝉",20,"中国");
        p4.show();
        System.out.println("------------------------------------");
    }
}

运行程序的结果如下:

当前人的姓名是西施,年龄是:18,所在的国籍是:中国
------------------------------------
当前人的姓名是杨贵妃,年龄是:28,所在的国籍是:中国
------------------------------------
当前人的姓名是王昭君,年龄是:25,所在的国籍是:中国
------------------------------------
当前人的姓名是貂蝉,年龄是:20,所在的国籍是:中国
------------------------------------

在当前的例子中,四个人的数据都是中国,这样就意味着,每次都要在堆内存中开辟空间并赋值同样的数据,就会浪费了空间。如果想办法让"country"只赋值一次,其他三个人共享这个值,就会节省空间。Java中有一个关键字"static"刚好可以满足需求。

static是静态修饰符,可以被多个对象”共用、共享“。被static修饰的变量就变成了特殊的 静态变量。所以上面的例子有了如下的改进:

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

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

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

    public void show(){
        System.out.println("当前人的姓名是"+name+",年龄是:"+age+",所在的国籍是:"+country);
    }
}

class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person("西施",18,"中国");
        p1.show() ;
        System.out.println("------------------------------------");
        Person p2 = new Person("杨贵妃",28);
        p2.show();
        System.out.println("------------------------------------");
        Person p3 = new Person("王昭君",25);
        p3.show();
        System.out.println("------------------------------------");
        Person p4 = new Person("貂蝉",20);
        p4.show();
        System.out.println("------------------------------------");
    }
}

这样会和改进之前的程序输出一样的结果。需要注意的一点是,在改进后的有country变量的构造方法就不要用类的实例访问静态成员变量了,而建议通过类"Person"直接访问静态成员变量。

为了弄清static的加载方式和总结特点,我们先对static关键字进行画图解析。

static关键字的内存图解

用上面的改进后的代码进行内存的图解:

在这里插入图片描述

上面的图解说明了:

  • 静态相关的东西跟对象无关,与类有关。

  • 类只加载一次

static关键字的特点
  1. 被static修饰的变量或者方法被称为“类变量或类方法”,因为他们是随着类的加载而加载,优先于对象存在的。

  2. static不能和this共存

    this代表的是当前类对象的地址值引用,创建了对象才能访问;this.变量名表示访问了当前类非静态的成员变量;this.方法名()表示访问当前类非静态的成员方法。

    static变量与类一起加载,优先进入内存。二者的生命周期不同,所以

    static与this不能共同使用。

  3. static可以被多个对象共享共用,这是static最基本的特点。

static关键字的注意事项
  1. 被静态修饰的变量与方法,一般推荐使用“类名.变量名”或者是“类名.方法名()”访问。不建议通过“对象名.变量名”或者“对象名.方法名”访问。

  2. 非静态的成员方法,既可以访问静态的变量或方法,也可以访问非静态的变量或方法。而静态的成员方法,只能访问静态的变量或方法。

    第2点可以总结为一句话:静态只能访问静态

static关键字的例子之main方法

开始写第一个Java程序时,就遇到了public static void main(String[] args)这一串神秘的英文,但是现在我们可以大致解析了。

public static void main(String[] args){}
解析:
public-->权限修饰符,被JVM调用,访问权限是最大的。
static-->静态修饰符,被JVM调用,不用创建对象,直接用类名去访问
void-->没有返回值的返回类型,被JVM调用,不需要给JVM返回值
main-->主方法的通用名称,虽然不是关键字,但是会被JVM识别
String[] args-->用于接收键盘录入的数据

面试题:静态变量和成员变量的区别

  1. 所属不同

    静态变量:属于类,所以也叫做类变量

    成员变量:属于对象,所以也叫做实例变量(对象变量)

  2. 内存中的位置不同

    静态变量:存储在方法区里的静态区

    成员变量:存储在堆内存

  3. 内存出现时间不同

    静态变量:随着类的加载而加载,随着类的消失而消失

    成员变量:随着对象的创建而存在,随着对象的消失而消失

  4. 调用不同

    静态变量:可以通过类名调用,也可以通过对象调用,但是建议通过类名调用

    成员变量:只能通过对象调用

帮助文档的制作(了解)

自定义工具类

以后在学习工具类时,会遇到很多静态方法,它们都是拿工具类名去访问。与此同时,我们自己其实也可以定义工具类,方便我们使用。

还没有学习类的时候,我们定义方法都是在定义静态的方法,因为定义的方法要在main方法中调用。如果在类中定义了一个非静态的方法,我们可以创建类对象,访问本类的成员方法。

但是,一般测试类时都是测试其它类,很少见到创建自己本类对象去访问。所以应该将本类定义的一些功能(非静态方法)单独定义到另一个类中。而这另一个类由于集成了一些功能,具有了工具的特性,所以叫做工具类。

工具类的特点:为了保证安全性,使外部类不能创建对象访问,工具类提供了构造方法,将无参构造私有化,而且还有文档注释。

帮助文档制作的方法

制作帮助文档首先要在类前和方法前添加文档注释,填写注释内容。有一些关键字可以帮助生成文档,这里拿几个说明一下。

文档注释中的部分关键字说明

在类前的文档注释:

/**
 * @author Chen
 * @date 2022/1/15 22:30
 * @version V 1.0
 * xxx
 */
public class ArrayTool{}
  • @author:作者

  • @date:创建java文件的时间

  • @version:版本号

  • xxx:类的说明

在方法前的文档注释:

/**
 * xxx
 * @param arr 要查询的指定的数组
 * @param target 要查询的指定的数组元素
 * @return  如果查找到了,返回的就是当前元素的下标,否则返回-1,找不到
 */
public static int getIndex(int[] arr,int target){}
  • xxx:方法的说明

  • @param xxx:方法参数的说明

  • @return:对方法返回值的说明

当然了,在IntelliJ IDEA中我们可以设置模板,自动生成文档注释,具体的操作可以自行百度。

然后对定义的工具类进行解析,解析步骤如下:

进入DOS窗口--->进入当前工具类所在的目录结构--->去掉包名--->保证当前解析的工具类是一个公共访问的(public类型)--->输入命令
    javadoc -d 目录名称  -author -version ArrayTool.java(java源文件)
--->以网页的形式自动生成jdk8文档说明书

在这里插入图片描述

代码块

在Java中使用{}包裹起来的就是代码块,根据其位置和声明的不同,可以分为局部代码块、构造代码块、静态代码块、同步代码块。

同步代码块在学习多线程时介绍

  • 局部代码块:在方法定义中的{}

    作用:限定变量的生命周期,使变量及早释放,提高内存的利用率。

  • 构造代码块:在类的成员变量(类中方法外)出现的{}

    作用:对成员的数据进行初始化

    特点:如果一个类中有构造代码块,先执行构造代码块,再执行构造方法。(构造代码块的优先级优于构造方法)

    开发中很少用到构造代码块,笔试题里出现较多

  • 静态代码块:在类中方法外出现,加了static修饰

    作用:用于给类进行初始化,在类加载时就执行,并且只执行一次。

代码块的练习
/**
 * 看程序写结果
 */

//定义一个类Code
class Code{

    //静态代码块
    static{
        int x =1000 ;
        System.out.println(1000);
    }

    //int num ;
    //定义一个无参构造方法
    public Code(){
        System.out.println("code...");
    }
    //类中,方法外--成员位置{} 构造代码块
    {
        int x = 100 ;
        System.out.println(x);
       // num = 20 ;//可以给成员变量赋值,实际开发中用的构造方法!
    }

    public Code(int num){
        System.out.println("code"+num);
    }

    //有参构造
    public Code(String name){
        System.out.println("code..."+name);
    }

    //构造代码块
    {
        int y = 200 ;
        System.out.println(y);
    }

    //静态代码块
    static{
        int y = 2000 ;
        System.out.println(y);
    }
}
public class CodeDemo {

    public static void main(String[] args) {

        //局部代码块
        {
            int x = 10;
            System.out.println(x);
            int y = 20;
            System.out.println(y);
        }
        System.out.println("-------------------------------") ;

        //通过无参构造方法new对象
        Code code = new Code() ;

        System.out.println("-------------------------------") ;

        Code code2 = new Code("hello") ;
    }
}

答案:

10
20
-------------------------------
1000
2000
100
200
code...
-------------------------------
100
200
code...hello

执行规律总结静态代码块>构造代码块>构造方法

继承

继承的概念

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,让这个类和多个类产生一种关系,这种关系就称为继承。有了继承关系的存在,多个类无需再定义这些属性和行为,只要继承那个类即可。在Java中,实现类与类的继承可以通过extends关键字。

继承的格式
	class 父类名{}
	class 子类名 extends 父类名{}
继承的案例
//父类
class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public void eat(){
        System.out.println("饿了就需要吃饭");
    }

    public void sleep(){
        System.out.println("困了就需要睡觉");
    }
}
//子类学生类
class Student extends Person{
    @Override
    public void eat() {
        System.out.println("学生需要吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("学生需要休息");
    }
}
//子类程序员类
class Programmer extends Person{
    @Override
    public void eat() {
        System.out.println("程序员需要吃饭");
    }

    @Override
    public void sleep() {
        System.out.println("程序员需要休息");
    }
}
//测试类
class TestPerson{
    public static void main(String[] args) {
        Programmer p = new Programmer();
        p.setName("爱学cs的小陈");
        p.setAge(18);
        System.out.println(p.getName()+"---"+p.getAge());
        p.eat();
        p.sleep();

        System.out.println("--------------------------------------");

        Student s = new Student();
        s.setName("爱学ee的小梦");
        s.setAge(20);
        System.out.println(s.getName()+"---"+s.getAge());
        s.eat();
        s.sleep();
    }
}

运行结果:

爱学cs的小陈---18
程序员需要吃饭
程序员需要休息
--------------------------------------
爱学ee的小梦---20
学生需要吃饭
学生需要休息
继承的好处
  • 提高了代码的复用性

  • 提高了代码的维护性

  • 让类与类产生了关系,这是多态的前提

    这也是继承的一个弊端:类的耦合性很强

设计原则:高内聚低耦合

简单理解:内聚就是自己完成某件事情的能力,耦合就是类与类之间的关系。所以可以这么解释:自己能干的就不要影响别人,这样别人将来改了代码,对我的影响也会最小。

博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值