Java学习笔记(2): 类与对象

时间:2023-02-24 08:35:21

Java中有基本类型与类类型两个类型系统,这章主要来谈类类型。Java是一门面向对象的语言,不是像C语言那样面向过程,所以更适合解决现实生活中的一些问题。


定义类

class Clothes {
String color;
char size;
}
定义类使用class关键词,后面的Clothes相当于为这个类取名。在这个类中,定义了两个值域成员(对象数据成员)。
new Clothes();
新建一个对象。
Clothes c1;
声明参考名称(参考变量/参考)。
Clothes c1 = new Clothes(); 
把c1参考至新建的对象,这个新建的对象就是Clothes。

一个原始码中可以有多个类定义,但只能有一个是公开类,且文档中的主文档名与公开类名称相同。


构造函数

class Clothes {
String color
char size
CLothes(String color,char size){
this.color = color;
this.size = size;
}
}

如果想在建立对象时一并进行某个初始流程,则可以定义像上面这样的构造函数。构造函数是与类名称同名的方法,区别构造函数与方法的一个特点就是构造函数没有返回值。

CLothes sun = new Clothes("res",'S');

使用指定构造函数建立对象。


使用标准类

Java SE提供了标准API,这些API就是由许多类组成的,可以直接取用这些标准类。

  • java.util.Scanner
    接受用户输入:System.in.read(),返回类型为int
    取得上一个字符串(空格或换行分割):scanner.next()
    取得用户输入的整行文字(换行分割):scanner.nextLine()

    包名称位java开头的类,表示标准API提供的类。

  • java.math.BigDecimal
    Java遵守IEEE754浮点数运算规范,使用分数和指数来表示浮点数,无限循环,无法精确表示,造成运算误差。如果要求精度,可以使用这个类。BigDecimal提供有plus(),substract(),multiply(),divide()等方法。


对象指定与相等性

在操作对象时,= 是用在指定参考名称参考至某个对象,而 == 是用在比较两个参考名称是否参考同一对象。而对于equals()来说,它是比较两个对象的”内含值”。


基本类型打包器

自动装箱

如果想让基本类型像对象一样操作,可以使用Long、Integer、Double、Float、Boolean、Byte等类来打包基本类型。

Integer data1 = 10;
Integer data2 = 20;

data1和data2在运行时会参考Integer实例,可以直接进行对象操作。

int i = 10;
Integer wrapper = i;

也可以使用更一般化的Number类来自动装箱。

Number number = 3.14f;

自动拆箱

Integer wrapper = 10; //自动装箱
int foo = wrapper; //自动拆箱

wrapper会参考至Integer,但它若被指定给int型的变量foo,则会自动取得打包的int类型再指定给foo,这叫做自动拆箱。

Boolean foo = true;
System.out.ptintln(foo && false);

先将foo拆箱,再进行&&运算。

自动装箱、拆箱的内幕

如果我们使用自动装箱或自动拆箱,其实编译程序会将代码展开为:

Integer wrapper = Integer.value();

在Java中,null代表一个特殊的对象,任何类声明的参考名称都可以参考至null,表明该名称没有参考至任何对象实体。

Integer i = null;
int j = i;
但是在执行时期会有错误,因为编译程序会展开:
Object i = null
int i = Integer.value();

由于i没有参考至任何对象,所以就不可能操作方法。


数组对象

数组基础

  • 增强式for循环
for(int score : scores)

意思是取得scores数组的第一个元素指定给score变量后执行循环体,接着取得第二个,以此类推,直到scores数组中所有元素都访问完为止。

  • 二维数组增强式for循环
for(int[] row : cords)
for(int value : row)

row参考到的对象是一维数组对象,外层for循环取得cords参考对象的每个索引,将参考到的对象指定给int[]类型的row名称

如果要设定值给数组中某个元素,要通过索引。

操作数组对象

  • 基本类型:
int[] scores = new int[10];
建立长度为10的数组:每个索引元素都会有默认值
int[] scores1 = new int[] {88,81,74};
new数组中一并指定初始值
int[] scores1 = {88,81,74};
int[] scores2 = scores1;
scores2[0] = 99;
System.out.println(scores1[0]);
//scores1[0]也等于99

数组是对象,而scores1和scores2是参考名称,将scores1指定给scores2,意思就是将scores1参考的对象也给scores2参考

  • 类类型:
Integer scores = new Integer[3];

建立Integer数组,每个索引都是参考至null,所以建立了0个Integer对象,而不是3个

Integer[][] scores = new Integer[3][2];
Integer二维数组代表一个Integer[][]类型的对象,其中有3个

Integer[]类型的索引,分别参考至长度为2的Integer一维数组对象,而每个Integer一维数组的索引都参考至null,所以还是建立了0个对象(实例)

数组复制

可以使用System.arraycopy()方法,使用原生方式复制每个索引元素,比自行循环来得快

int[] scores1 = {88,76,33};
int[] scores2 = new int[scores1.length];
System.arraycopy(scores1,0,scores2,0,scores1.length);
五个参数分别是来源数组,来源起始索引,目的数组,目的起始索引,复制长度

还有个更方便的Arrays.copyOf()方法

int[] scores1 = {88,76,33};
int[] scores2 = Arrays.copyOf(scores1,scores1.length);

这两种复制方法都是执行浅层复制。
那么什么是浅层复制?是否还有深层复制?

区分浅层复制和深层复制:
如果有两个参考c1和c2

  • 浅层复制:循环中仅将c1每个索引处所参考的对象,也给c2每个索引来参考,并没有实际复制出对象,也叫复制参考;System.arraycopy()和Arrays.copyOf()用在类类型声明的数组时,都是执行浅层复制
  • 深层复制:c1每个索引参考的对象会被复制,分别指定给c2每个索引

字符串对象

字符串常量与字符串池

在java中,以”“包括的字符串,只要内容相同,无论在程序中出现几次,JVM都只会建立一个String实例,并在字符串池中维护。

String name1 = "justin";
String name2 = "justin";
System.outp.println(name1 == name2);
//这两个字符串是参考到同一对象的,答案是true。

为了节省内存,当再一次定义相同序列的字符串时,它不会新建对象,所以上面两个字符串定义实际上是相等的。

不可变动字符串

在Java中,字符串对象一旦建立,就无法更改对象中的任何内容,对象上没有任何方法可以更改字符串的内容,但是却可以使用+连接字符串。
其实使用+连接字符串是会产生新的String实例,所以如果在循环或是递归中使用+连接字符串的话,就会不断的产生新对象,这会造成效能上的负担。