JAVA基本程序设计结构与类
这学期计网、数据库都安排上了,这两门课最好还是在认真学习课本的基础上多做一些实践吧,所以就决定来学学JAVA(正好大创项目中也能用得到,后面也有计网课设,感觉还是很有必要的),这里主要的学习资料是书籍:《JAVA核心技术 卷Ⅰ》
注:作为一名c++选手,这里主要是总结了一些JAVA和C++不同的一些东西或者JAVA非常重要的一些东西,主要就是一个个人笔记吧~
1. 基本程序设计结构
1.1 整型
整形数据主要分为四种
类型 | 存储需求 |
---|---|
byte | 1字节 |
short | 2字节 |
int | 4字节 |
long | 8字节 |
比cpp少了不少,不区分有符号和无符号类型,而且各种类型的大小也与机器无关(java程序致力于保证程序在所有机器上的运行结果相同,尽管这一点在浮点数可能回存在偏差)
长整型long需要添加了l
或L
,后缀,并且可以配合 _
给数组做分隔,便于阅读;十六进制前缀: 0x,0X
, 八进制前缀: 0
,二进制前缀: 0b, 0B
。可以通过下面代码快速熟悉一下!
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);
}
}
输出结果:
a=4000000000000000
b=255
c=8
d=15
1.2 char类型
和cpp不同,一个char类型是2个字节,由于JAVA采用的UTF-16编码,所有有的字符只需要一个char类型变量就能表示,有的就需要用两个char类型变量!
1.3 常量
JAVA使用关键字 final
指示常量(只能被赋值一次,不能再次修改!)
public class helloworld {
public static void main(String[] args) {
final double PI;
PI = 3.14; // 常量只能被赋值一次
System.out.println(PI);
}
}
1.4 数值类型之间的转换
我们有时候就会有这样数值转换的需求,可以参考下图(来自JAVA核心技术),其中实线代表无信息丢失转换,虚线代表有信息丢失转化(值得注意的是int转float会有信息丢失)
1.5 数组
JAVA的数组和cpp的数组有所不同,直接来看吧。
首先从一维数组开始,一般定义都是使用 int[] a
(当然也可以使用int a[]
),可以选择初始化或者不初始化,还有一种匿名数组的形式(感觉类似于对象)
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);
}
}
输出结果:
100
5
5
对于多维数组也是类似的,但是JAVA存在不规则数组,直接看样例代码吧,比较好懂
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, 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类,方面后面一些东西的说明
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 显式参数和隐式参数
当我们调用一个对象的方法时,比如如下语句:
employee a = new employee("Jack", 2000, 2018, 10, 25);
a.raiseSalary(20); // 调用
raiseSalary
方法就有两个方法,第一个参数称为隐式(implicit)参数,是出现在方法名前的employee类型对象(即样例中的a),第二个参数是方法名括号中的数值,这是一个显示(explicit)参数(即样例中的20)。所以隐式参数称为方法中的调用者或接收者。
在每一个方法中,关键词 this
指示隐式参数(当然也可以省略!),比如这里的 raiseSalary
函数:
public void raiseSalary(double percent){
this.salary = (percent/100 + 1) * this.salary;
}
2.2 关于封装
封装的优点:
- 提高了数据的安全性 别人不能够通过 变量名.属性名 的方式来修改某个私有的成员属性
- 操作简单 封装后,多个调用者在使用的时候,只需调用方法即可,调用者不需要再进行判断
- 隐藏了实现 实现过程对调用者是不可见的,调用者只需调用方法即可,不知道具体实现过程
如何实现封装?比如说想获得以及设置一个实例字段的值,就需要提供以下三项内容:
- 一个私有的数据字段(数据的安全性)
- 一个公共的字段访问器方法
- 一个公共字段的更改器方法
但是值得注意的是,不要编写可变对象的访问器变量,比如我们将上述定义的 employee
类的 hireDay
修改为Date()对象,然后再使用getHireDay()返回,我们就会发现,我们就能直接在外部不调用器更改器方法的情况下修改 hireDay
。
..
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对象被修改
}
}
输出结果:
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就没问题,因为这两个类都没有修改器方法,因此也就保证了类的封装!)
...
public Date getHireDay(){
return (Date)hireDay.clone(); // 直接返回date对象的副本
}
...
输出结果(保证了类的封装性):
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 类的权限访问
我们首先来看一个栗子
...
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(之前我们用其定义常量),这样的字段必须在构造对象时初始化,之后不能再修改这个字段!
举个栗子:
public class employee {
private final String name;
...
}
final修饰符对类型为基本类型或者不可变类型的字段尤其有用(比如这个栗子中的String)
但是对于可变的类,使用final修饰符就会造成混乱!! 看下面这个栗子:
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类数据字段,接下来我们尝试修改!
public class helloworld {
public static void main(String[] args) {
employee a = new employee("Jack", 2000, 2018, 10, 25);
a.show();
a.changeHireDay();
a.show();
}
}
输出结果:
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,每个类只有一个这样的字段,我们看下面这个栗子:
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();
}
}
输出结果:
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,可以直接用类名访问变量!举个栗子
public class employee {
...
public static final String slogan = "好好学习,天天向上";
...
}
public class helloworld {
public static void main(String[] args) {
System.out.println(employee.slogan);
}
}
输出自然就是(这里这是了final,所有使用public字段没有关系):
好好学习,天天向上
静态方法
静态方法就是不再对象上执行的方法,比如Math类中的pow方法(Math.pow(x, a)
计算x^a),调用时不使用任何的Math对象,也就是说其没有隐式参数,但是静态方法不能访问实例字段,只能访问静态字段!
举个栗子:
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();
}
}
输出结果:
我们的口号是:好好学习,天天向上
2.7 文档注释
类注释:
/**
* A object represents a employee's info
*/
public class employee {
....
}
方法注释:
/**
* 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){
...
}
当然还有字段注释,通用注释等等~