JAVA基本程序设计结构与类

JAVA基本程序设计结构与类

这学期计网、数据库都安排上了,这两门课最好还是在认真学习课本的基础上多做一些实践吧,所以就决定来学学JAVA(正好大创项目中也能用得到,后面也有计网课设,感觉还是很有必要的),这里主要的学习资料是书籍:《JAVA核心技术 卷Ⅰ》

注:作为一名c++选手,这里主要是总结了一些JAVA和C++不同的一些东西或者JAVA非常重要的一些东西,主要就是一个个人笔记吧~

1. 基本程序设计结构

1.1 整型

整形数据主要分为四种

类型 存储需求
byte 1字节
short 2字节
int 4字节
long 8字节

比cpp少了不少,不区分有符号和无符号类型,而且各种类型的大小也与机器无关(java程序致力于保证程序在所有机器上的运行结果相同,尽管这一点在浮点数可能回存在偏差)

长整型long需要添加了lL,后缀,并且可以配合 _ 给数组做分隔,便于阅读;十六进制前缀: 0x,0X , 八进制前缀: 0 ,二进制前缀: 0b, 0B 。可以通过下面代码快速熟悉一下!

1
2
3
4
5
6
7
8
9
10
11
12
public class helloworld {
public static void main(String[] args) {
long a = 4_000_000_000_000_000L; // 长整型需要加 l 或 L 后缀,并且可以配合 _ 辅助阅读
int b = 0xff; // 十六进制
int c = 010; // 八进制
int d = 0b1111; // 二进制
System.out.println("a=" + a);
System.out.println("b=" + b);
System.out.println("c=" + c);
System.out.println("d=" + d);
}
}

输出结果:

1
2
3
4
a=4000000000000000
b=255
c=8
d=15

1.2 char类型

和cpp不同,一个char类型是2个字节,由于JAVA采用的UTF-16编码,所有有的字符只需要一个char类型变量就能表示,有的就需要用两个char类型变量!

1.3 常量

JAVA使用关键字 final 指示常量(只能被赋值一次,不能再次修改!)

1
2
3
4
5
6
7
public class helloworld {
public static void main(String[] args) {
final double PI;
PI = 3.14; // 常量只能被赋值一次
System.out.println(PI);
}
}

1.4 数值类型之间的转换

我们有时候就会有这样数值转换的需求,可以参考下图(来自JAVA核心技术),其中实线代表无信息丢失转换,虚线代表有信息丢失转化(值得注意的是int转float会有信息丢失)

0npm6K.png

1.5 数组

JAVA的数组和cpp的数组有所不同,直接来看吧。

首先从一维数组开始,一般定义都是使用 int[] a(当然也可以使用int a[]),可以选择初始化或者不初始化,还有一种匿名数组的形式(感觉类似于对象)

1
2
3
4
5
6
7
8
9
10
11
public class helloworld {
public static void main(String[] args) {
int[] a = new int[100]; // 创建一个数组
int[] b = {1, 2, 3, 4, 5}; // 初始化
int[] c;
c = new int[] {6, 7, 8, 9, 10}; // 匿名数组
System.out.println(a.length);
System.out.println(b.length);
System.out.println(c.length);
}
}

输出结果:

1
2
3
100
5
5

对于多维数组也是类似的,但是JAVA存在不规则数组,直接看样例代码吧,比较好懂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class helloworld {
public static void main(String[] args) {
int[][] square = { // 二维数组
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 16}
};
System.out.println(Arrays.deepToString(square)); // 快读打印二维数组

int count = 1;
int[][] irregularSquare = new int[4][]; // 不规则二维数组
for(int i = 0; i < 4; i++){
irregularSquare[i] = new int[i+1];
for(int j = 0; j < i+1; j++){
irregularSquare[i][j] = count;
count++;
}
}
System.out.println(Arrays.deepToString(irregularSquare));
}
}

输出结果:

1
2
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]

1.6 其他

其他的一些条件控制基本上和C++是相同的(if, for, for each等等都差不多),switch可能有些区别,每一个case之后的语句无需用 {} 括起来,这里就不赘述了!

2. 类

我们首先简单的定义一个简单的employee类,方面后面一些东西的说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.time.LocalDate;

public class employee {
private String name;
private double salary;
private LocalDate hireDay;

public employee(String name, double salary, int year, int month, int day){
this.name = name;
this.salary = salary;
this.hireDay = LocalDate.of(year, month, day);
}
public String getName(){
return name;
}
public double getSalary(){
return salary;
}
public LocalDate getHireDay(){
return hireDay;
}
public void raiseSalary(double percent){
this.salary = (percent/100 + 1) * this.salary;
}
public void show(){
System.out.println("name:"+ this.name+" salary:"+this.salary);
System.out.println("hireDay:" + this.hireDay);
}
}

2.1 显式参数和隐式参数

当我们调用一个对象的方法时,比如如下语句:

1
2
employee a = new employee("Jack", 2000, 2018, 10, 25);
a.raiseSalary(20); // 调用

raiseSalary 方法就有两个方法,第一个参数称为隐式(implicit)参数,是出现在方法名前的employee类型对象(即样例中的a),第二个参数是方法名括号中的数值,这是一个显示(explicit)参数(即样例中的20)。所以隐式参数称为方法中的调用者或接收者。

在每一个方法中,关键词 this 指示隐式参数(当然也可以省略!),比如这里的 raiseSalary 函数:

1
2
3
public void raiseSalary(double percent){
this.salary = (percent/100 + 1) * this.salary;
}

2.2 关于封装

封装的优点:

  • 提高了数据的安全性 别人不能够通过 变量名.属性名 的方式来修改某个私有的成员属性
  • 操作简单 封装后,多个调用者在使用的时候,只需调用方法即可,调用者不需要再进行判断
  • 隐藏了实现 实现过程对调用者是不可见的,调用者只需调用方法即可,不知道具体实现过程

如何实现封装?比如说想获得以及设置一个实例字段的值,就需要提供以下三项内容:

  • 一个私有的数据字段(数据的安全性)
  • 一个公共的字段访问器方法
  • 一个公共字段的更改器方法

但是值得注意的是,不要编写可变对象的访问器变量,比如我们将上述定义的 employee 类的 hireDay 修改为Date()对象,然后再使用getHireDay()返回,我们就会发现,我们就能直接在外部不调用器更改器方法的情况下修改 hireDay

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
public class employee {
...
private Date hireDay;

public employee(String name, double salary, int year, int month, int day){
...
this.hireDay = new Date(year, month, day); // 修改为date对象
}
...
public LocalDate getHireDay(){
return hireDay; // 直接返回date对象
}
...
}
// 然后调用
public class helloworld {
public static void main(String[] args) {
employee a = new employee("Jack", 2000, 2018, 10, 25);
a.show(); // 查看初始Date对象
Date b = a.getHireDay(); // 获取器Date对象
long oneYearMilliSeconds = 365L * 24L * 60L * 60L * 1000L;
b.setTime(b.getTime() - oneYearMilliSeconds); // 在外部修改Date对象
a.show(); // 发现Date对象被修改
}
}

输出结果:

1
2
3
4
name:Jack   salary:2000.0
hireDay:Mon Nov 25 00:00:00 CST 3918
name:Jack salary:2000.0
hireDay:Sun Nov 25 00:00:00 CST 3917

可以看到Date对象就被修改了,这样就破环了封装性!,所有不要直接返回对象的引用,可以返回其副本,如下:(之前返回LocalDate类以及String就没问题,因为这两个类都没有修改器方法,因此也就保证了类的封装!)

1
2
3
4
5
...
public Date getHireDay(){
return (Date)hireDay.clone(); // 直接返回date对象的副本
}
...

输出结果(保证了类的封装性):

1
2
3
4
name:Jack   salary:2000.0
hireDay:Mon Nov 25 00:00:00 CST 3918
name:Jack salary:2000.0
hireDay:Mon Nov 25 00:00:00 CST 3918

2.3 类的权限访问

我们首先来看一个栗子

1
2
3
4
5
6
...
employee a = new employee("Jack", 2000, 2018, 10, 25);
employee b = a;
if(a.equals(b))
System.out.println("a equals b");
...

显然程序会输出”a equals b”,但是这是如何判断 a 等于 b 的呢?类不是封装好了吗?而且数据字段都设置为了私有鸭!

原因是私有只是对于“外界”来说的,employee类的方法可以访问任何employee对象的私有字段!

JAVA的四种访问控制修饰符:

  • private:仅对本类可见
  • public:对外部完全可见
  • protected:对本包和所有子类可见
  • 默认:对本包可见

2.4 final字段

我们可以将实例字段定义为final(之前我们用其定义常量),这样的字段必须在构造对象时初始化,之后不能再修改这个字段!

举个栗子:

1
2
3
4
public class employee {
private final String name;
...
}

final修饰符对类型为基本类型或者不可变类型的字段尤其有用(比如这个栗子中的String)

但是对于可变的类,使用final修饰符就会造成混乱!! 看下面这个栗子:

1
2
3
4
5
6
7
8
9
public class employee {
...
private final Date hireDay;
...
public void changeHireDay(){
long oneYearMilliSeconds = 365L * 24L * 60L * 60L * 1000L;
this.hireDay.setTime(this.hireDay.getTime() - oneYearMilliSeconds);
}
...

我们将Date类数据字段设置为了final,然后我们有编写了一个方法修改Date类数据字段,接下来我们尝试修改!

1
2
3
4
5
6
7
8
public class helloworld {
public static void main(String[] args) {
employee a = new employee("Jack", 2000, 2018, 10, 25);
a.show();
a.changeHireDay();
a.show();
}
}

输出结果:

1
2
3
4
name:Jack   salary:2000.0
hireDay:Mon Nov 25 00:00:00 CST 3918
name:Jack salary:2000.0
hireDay:Sun Nov 25 00:00:00 CST 3917

修改成功了!!!

final关键字只是表示存储在 hireDay 变量中的对象引用不会在指示另一个不同的Date对象,但是这个对象可以更改!!(所以final字段常用于基本类型以及不可变类字段)

2.5 静态字段

如果将一个字段定义为static,每个类只有一个这样的字段,我们看下面这个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class employee {
...
private int id;
private static int nextId = 1;
public employee(String name, double salary, int year, int month, int day){
...
this.id = this.nextId;
this.nextId++;
}
...
}
public class helloworld {
public static void main(String[] args) {
employee a = new employee("Jack", 2000, 2018, 10, 25);
a.show();
employee b = new employee("Bob", 3000, 2016, 8, 7);
b.show();
}
}

输出结果:

1
2
3
4
5
6
name:Jack   salary:2000.0
hireDay:Mon Nov 25 00:00:00 CST 3918
ID:1
name:Bob salary:3000.0
hireDay:Thu Sep 07 00:00:00 CST 3916
ID:2

我们可以看到每个人的ID是不同的,说明nextId字段的值改变了!

每一个employee对象都有一个自己的 id 字段,但是这个类的所有实例都将共享一个 nextId 字段,就算即使存在employee对象,静态字段 nextId 字段也存在,它属于类,而不属于任何单个的对象!

2.6 静态常量与静态方法

静态常量

相较于静态变量,静态常量就很常用,比如Math类中的PI,可以直接用类名访问变量!举个栗子

1
2
3
4
5
6
7
8
9
10
public class employee {
...
public static final String slogan = "好好学习,天天向上";
...
}
public class helloworld {
public static void main(String[] args) {
System.out.println(employee.slogan);
}
}

输出自然就是(这里这是了final,所有使用public字段没有关系):

1
好好学习,天天向上

静态方法

静态方法就是不再对象上执行的方法,比如Math类中的pow方法(Math.pow(x, a)计算x^a),调用时不使用任何的Math对象,也就是说其没有隐式参数,但是静态方法不能访问实例字段,只能访问静态字段!

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class employee {
...
public static final String slogan = "好好学习,天天向上";

public static void ourSlogan(){
System.out.println("我们的口号是:"+slogan);
}
...
}
public class helloworld {
public static void main(String[] args) {
employee.ourSlogan();
}
}

输出结果:

1
我们的口号是:好好学习,天天向上

2.7 文档注释

类注释:

1
2
3
4
5
6
/**
* A object represents a employee's info
*/
public class employee {
....
}

方法注释:

1
2
3
4
5
6
7
8
9
10
11
    /**
* The employee class constructor
* @param name the name of the employee
* @param salary the salary of the employee
* @param year the year of the employee joined the company
* @param month the month of the employee joined the company
* @param day the day of the employee joined the company
*/
public employee(String name, double salary, int year, int month, int day){
...
}

当然还有字段注释,通用注释等等~