对象与类(Java学习-2)


面向对象程序设计概述

谈到类和对象,自然而然的想到面向对象程序设计(OOP),百度百科给的解释是

面向对象程序设计(Object Oriented Programming)作为一种新方法,其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事物的一切具体特征,只能对事物特征和变化规律的一种抽象,且在它所涉及的范围内更普遍、更集中、更深刻地描述客体的特征。通过建立模型而达到的抽象是人们对客体认识的深化。

我个人的看法是,C语言是面向过程的,更多的是算法+数据结构,一个程序成百上千的“过程”,以至于特别的繁琐;某天某人推出了一个“面向对象的架构”,先在关系数据库验证了一下下,发现把显示世界抽象为机器世界蛮方便的,只要一个模版,一切都可以化身为对象, 那么这也需要我们有“上帝视角”,提前对此抽象、模型。

so,面向对象,更多的是考虑数据结构,而非算法。

  • 类(class)

类是什么,看看刚刚百度百科的定义呗。

其本质是以建立模型,体现出来的抽象思维过程和面向对象的方法。

《Java核心技术》的定义:类是构造对象的模版或者蓝图。由类构造对象的过程称为类的实例化。

  • 对象

对象,也就是现实世界的抽象化实体,对象有三个特征(不是面向对象),分别为

对象的状态:描述当前特征信息。(正常调用方法才会改变,非正常改变就是封装有问题)

对象的行为:对象里面有什么方法,可以对它施加那些操作。

对象标识 :唯一识别身份的。(数据库中的主码/操作系统中的进程控制块?)

预定义类

说完上面的概念的东西,那就看看Java语言中的类,String类用的多,那就看看这个吧!

public final class String {//删除了非关键信息
     private final char value[];
     private int hash;
     private static final long serialVersionUID = -6849794470754667710L;
     public String() {
        this.value = "".value;
    }
     public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
     public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
}

String类是一个很大的类,里面有很多的方法,上面删除了很多东西,切记!

  • 对象与构造器
String str = new String();//方式一:实例化对象
String str = new String(str);//方法二:实例化对象

上面是两种使用构造器生成对象的方法。

String() 称为构造器,无返回值。

new 称为操作符,开辟新堆内存,返回一个引用(一个栈,存堆地址)。

new String()是在堆中开辟一块空间,把String对象存进去,返回值是一个引用(一个栈,存堆地址)。

String str为声明String类型变量,并存储这个引用。

堆内存空间:保存真正的数据,对象的属性信息。

栈内存空间:保存的堆内存的地址,堆内存操作权,简单理解为‘对象名称’。

这样我们就生成一个对象了,这里可以看到“方法二:实例化对象”,是可以把同类型对象作为参数传入,创建新对象的,类似构造器中参

数个数和参数类型不同,这种特征叫做“重载”,后面再讲!!(有意思,构造函数的隐式参数的概念this。)

注意,如果编写一个类没有编写构造器,那么系统会提供一个无参数构造器。

  • 实例域
int hash;
private int hash;     
private final char value[];
public static final long serialVersionUID = -6849794470754667710L;

上面hash,hash,value称为实例域,默认值分别为0,0,\u0000(空格)。

private称为私有的,本类对象才可以调用,外部类不可以使用,这也就是“封装”。

final修饰符,“最终的,最后的,常量”,表示存储在value变量中的字符数组对象引用不会再指示其他(一个栈,存堆地址),由于不能改变栈,只能通过对象中的方法进行改变数组内容。

serialVersionUID,使用了static和final修饰符,称为静态常量/静态域。静态域或者静态常量都是属于类的,而不是属于任何独立对象的,它只生成一份,大家用同一个值/引用。

long num = String.serialVersionUID;//静态域,属于类,不需要对象,即可直接调用

下面介绍的修饰符,也可以使用在类名、方法名前面。(后面再讲)

访问权限

实例域的值,可以通过构造器或者方法等进行修改。

  • 方法
 public int hashCode() {}

用户自定义类

只要有构造器,那么自己的类就算是完成了,可以正常的生成对象的,构造器和类是同名的。

有属性(实例域)和函数(方法),那么就可以对现实世界进行抽象,使用构造器生成对象了。

我们可以把Java提供的String类改变一下就是用户自定义类。

public final class String2 {//删除了非关键信息
     private final char value[];
     public String2() {
        this.value = "".value;
    }
     public int hashCode() {
        ......
    }
}

这样,我们就创建了一个类。每一个类中都有一个默认的构造器(构造方法)。

这个类编译了之后,会通过Java编译器,生成一个String2.class文件。(字节码文件)

当创建这个类实例,会通过String2.class文件,使用字节码解释器,生成机器码文件,系统就可以运行。

方法(重点)

方法比较重要,特地拿出来细谈。

  • main方法
public static void main(String[] args) {
	
}

main方法是一个程序的入口,会根据方法中的语句,依此执行,每个类都可以有一个main方法(在类中直接单元测试)。

  • 静态方法

main方法前面有static修饰符,表示此方法是静态的。

Java预定义类 ,Math类中有很多静态方法,我们就拿Math类学习学习呗!

public final class Math {
    public static final double PI = 3.14159265358979323846;
    public final double PI = 3.14159265358979323846;//注意
    ......
    public static int max(int a, int b) {
        return (a >= b) ? a : b;
    }
}

根据上面的静态域,可以推出静态方法的特性。同理可得

在下面两种情况下使用静态方法

1.一个方法不需要访问对象状态(不需要对象),其所需的参数都是通过显式参数提供的。

int a = Math.max(4,8);//返回最大的数,显示参数

2.一个方法只需要访问类的静态域

double pi = Math.PI;
  • 工厂方法(静态工厂)

这个留到设计模式再说,其实就是工厂流水线的形式,生产标准的对象,带上静态工厂呢!就是类不需要访问对象状态,自己访问静态方法,方法中设置对象的生产步骤.....。

(new LocalDate).new//工厂模式
NumberFormat.getGurrencyInstance//静态工厂
  • 方法参数(重点,值传递和引用传递的介绍)

方法参数,什么是方法参数呢??

public final class Math {
    ......
    public static int max(int a, int b) {
        return (a >= b) ? a : b;
    }
}

类似Math类,调用静态方法Math.max(1,5),1和5就是参数,称为实参,实际传过去的值。

max(int a, int b)中a、b称为形参,形式上的参数,变量名可以随便,规定调用此方法需要传递的数据类型和个数。

方法参数中,有一个概念,引用传递和值传递。

  • 值传递
package com.yingqi;

public class Demo {
	int a = 1;
	int b = 3;

	public void Multiple(int a, int b) {
		a = a * 3;
		b = b * 3;
		System.out.println("a=" + a + ",b=" + b);// 输出a=3,b=9
	}

	public static void main(String[] args) {
		Demo demo = new Demo();
		demo.Multiple(demo.a, demo.b);
		System.out.println("a=" + demo.a + ",b=" + demo.b);// 输出a=1,b=3
	}
}

从main函数开始,我们调用Multiple函数,把a,b的值穿进去,并且放大3倍,第一次在Multiple中输出3倍的结果,但是第二次输出并没有放大3倍,还是原来的数字,我们从内存分析看吧。

值传递

其实,可以看到方法中的a,b是拷贝 对象中的ab值。

  • 引用传递
package com.yingqi;

public class Demo {
	String str = "hello";

	public void Quote(String str) {
		str = "word!!";
		System.out.println(str);//输出word!!!
	}

	public static void main(String[] args) {
		Demo demo = new Demo();
		demo.Quote(demo.str);
		System.out.println(demo.str);//hello
	}

}

看样子,好像和刚刚没什么区别嘛,你哄我喔!但是,我可以说引用传递绝大多数会发生改变的,那为什么这个没有改变呢?字符串

我可以明确告诉你是字符串的问题,讲个正常的再解释这个!

package com.yingqi;

public class Demo {
	
	public void Quote(StringBuilder str2) {
		str2.append(" word!!");
		System.out.println(str2);//hello word!!
	}

	public static void main(String[] args) {
		StringBuilder str1 = new StringBuilder("hello");
		Demo demo = new Demo();
		demo.Quote(str1);
		System.out.println(str1);//hello word!!
	}

}

那这个为什么输出相同的//hello word!!呢,我们也用内存介绍吧。

引用传递

这图我画的好丑啊,但也可以看。

我们的引用传递,方法中的形参也是拷贝了一份相同的值(一个栈,存堆地址值),只不过这个值是指向同一个对象的,我们是改变了对象的内容,并没有改变这个值。那么输出结果就是Hello World!!

所以,Java的参数传递,只传递的是值,不是引用,我们是习惯了这样,叫这个名称罢了。

回到上一题,字符串的问题,str="word!!"相当与str= new String();也就相当改变了传递的值(回到值传递)!

我们先从一段代码中,学习什么是包,以及怎么使用,有什么用。

package java.lang;
import java.util.Random;

import sun.misc.FloatConsts;
import sun.misc.DoubleConsts;

public final class Math {
    ......
}

package java.lang这个就是包路径,Math类的源码、.calss文件都在这个目录的文件夹下。

包有什么作用,我们知道Java语音是发展了很久的,有很多类,也就是很复杂,一般复杂的东西,我们都会想到分层概念,包呢?也可以这样理解,每个包之间都是独立的,不同包中可以有相同名称的类。

  • 导入包
import java.util.Random;

import sun.misc.FloatConsts;
import sun.misc.DoubleConsts;

使用import关键字,后面接着包路径和名称,这样的语句就是导入包了,现在大家都用ide等开发工具,一般情况下,都会自动导包的,当遇到类冲突的时,我们需要手动导包,这个过程就不说了,慢慢就熟悉。

  • 静态导入包
package com.yingqi;
import static java.lang.System.*;
public class Demo {
	public static void main(String[] args) {
		out.print("yes");//yes
	}
}

import static 包路径 这样的语句,我们称为静态导入,导入包的静态资源(静态域,静态方法),那就可以直接使用了。

  • 把一个类放入包中
package java.lang;

类似的,package 包路径 这样的语句,我们称为把这个类放入到这个lang包中,包路径我们常用反写域名的形式。com.sum.*。

  • 包的作用域
public class Math {
    ......
}

class关键字前面public修饰符,代表可以被任意的类使用。

这些修饰符也可以使用在类名、方法名、域... 前面(后面再讲)

访问权限

这后面应该还可以谈一下类路径(ClassPath),很多语言都涉及到这个,但说出来却很复杂,我也有点绕(类加载器、全局设置、打包等...),一般我们使用ide等开发工具都不会涉及到这个问题。

总结

这些是很基础的东西,绝大多是编程语言都支持,并且相差不会很大,上面说的是远远不够的,大家要看更优秀的作品,这只是自己一年前的总结(原因在这里),这些基础敲多了,就自然的熟悉了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券