我有两颗糖

博客小屋

By 某只不愿露脸的尹同学


07-Java语法基础

1. 初识 Java

Java是一种可以开发跨平台应用软件的面向对象程序设计语言,是一种强类型的语言。

1.1 第一个Java 程序

打开终端,输入

$ vim HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        // static提供了这样一个特性,无需建立对象,就可以启动
        system.out.println("Hello world!");		
    }
}

在终端将上述代码存放到 Hello.java中,使用 javac Hello.java编译代码,生成一个 Hello.class文件,接着使用 java Hello来运行程序。

$ javac Hello.java	## 编译
$ java Hello		## 运行

在编写代码时可能需要使用 package:

import java.io.*	// 导入java_installation/java/io下的所有类

1.2 Java 源程序与编译型运行区别

基础语法

  • 对大小写敏感
  • 类名应当大写开头
  • 方法名要小写开头
  • 源文件名要和 public 类名相同
  • 标识符由字母、下划线或 $ 打头,不能是关键字

2. Java 对象和类

看一个简单的类:

public class Dog {

	String name;	// 实例变量
	String color;
	int age;

    // 构造器 public 类型
	public Dog(String name, int age, String color) {
		this.name = name;	// 参数名和实例变量名相同时,使用 this
        this.age = age;
        this.color=color;
	}

	public void run() {
		System.out.println(name + " is running...");
	}

	public int getAge() {
		System.out.println("Age: " + age);
		return age;
	}

	public static void main(String[] args) {

		Dog myDog = new Dog("Tom", 2, "Brown");   // 创建对象
        System.out.println(myDog.color);
		myDog.getAge();     // 调用函数
		myDog.run();
	}
}

这个类描述了小狗,属性包含:名字、颜色和年龄,类里面的函数称为方法。

static 类型的 main 函数不需要调用,自动执行。

this 关键字

当实例变量和局部变量重名,JAVA平台会按照先局部变量、后实例变量的顺序寻找。

如果使用 this.name,则不会在方法(局部变量)中寻找变量name,而是直接去实例变量中去寻找。

当实例变量和局部变量名字不重复时,可以不使用 this,编译器会自动加上 this


3. 数据与变量

3.1 基本数据类型

public class Data{
    public static void main(String[] args){
        int a = 65;
        short b = 0;
        float c = 2.5;
        int d = 100;    // decimal
        int e = 054;    // octal
        int f = 0x30;   // hex
        char ch = 'a';  // 占 2 字节 使用 Unicode 可以存放一个汉字
        String str = "Hello";
        boolean full = false;

        byte a1 = (byte)a;  // 强制转换

        final double PI = 3.1459265;    // const

        // 可以查看特定类型数据的长度和范围
        System.out.println(Integer.MAX_VALUE);
        System.out.println(Double.SIZE);
    }
}

Java 为每一个内置数据类型都提供了对应的包装类:Boolean, Byte, Short, Integer, Long, Character, Float, Double.

public class Test{
    public static void main(String[] args){
        Double x = 5.3;
		System.out.println(Math.floor(x));
    }
}

Java Math 类:

public class Test{
    public static void main(String[] args){
        System.out.println("PI = " + Math.PI);
        System.out.println("cos(PI/2) = " + Math.tan(Math.PI/4));
        System.out.println("random: " + Math.random());
        System.out.println("exp()")
    }
}

3.2 基本变量类型

  • 类变量:独立于方法之外的变量,用 static 修饰;
  • 实例变量:独立于方法之外的变量,不过没有 static 修饰;
  • 局部变量:类的方法中的变量。
public class Var{
    static int count = 0;  // 类变量
    String str = "Tom";    // 实例变量

    public void method(){
        int i = 0;         // 局部变量
    }
}

类变量属于静态变量,无论一个类有多少个对象,类变量都共用一个!


4. Java 修饰符

4.1 访问控制修饰符

Java 支持四种不同的访问权限:

default:在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。

private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

public : 对所有类可见。使用对象:类、接口、变量、方法

protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。

4.2 非访问修饰符

static

静态变量:static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。

静态函数:static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。

public class INstenceCOunter{
    private static int numInstences = 0;    // 静态变量

    prvate static void addInstence(){       // 静态函数
        numInstences++;
    }
}

注意:在静态方法中调用非静态方法时,编译器会报错(因为非静态方法只有实例化的对象能够调用,而静态方法执行的过程中未调用构造函数实例化对象,因此不允许调用!)

解决办法可以参考 博客 ,要么创建一个类的对象,通过对象来调用函数,要么将非静态方法声明中加上 static 改为静态方法,同时将变量声明为全局静态的。

final

被 final 修饰的实例变量必须显式指定初始值,之后不能再修改

public class Test{
    final double PI = 3.14159;   // 不能修改值

    // final 方法不能被子类重写
    public final void changeName(String name){
        this.name = name;
    }
}

5. 流程控制

5.1 循环结构

while (true){
    count--;
    if (count == 0)
        break;
}

for (int i=0; i<n; i++){
    count--;
}

String [] names = {"Tom", "Domy", "Sam"};
for (String name : names){
    System.out.print(name + " ");
}

5.2 分支结构

常用的分支结构如下:

if (a == 1)
    return 1;
else if (a == 0)
    return -1;
else
    return 0;

switch (expression){
    case value1:
        // code_block
        break;
    case value2:
        // code_block
        break;
    default:
        // code_block
        break;
}

6. Java 方法

6.1 方法的定义与调用

一个方法包含了 修饰符返回值类型方法名参数方法体构成

在不创建类对象的情况下,主函数可以调用静态方法

public class MethodTest {

	public static int max(int a, int b) {
		return a > b ? a : b;
	}
	public static void main(String[] args) {
		int a = 2;
		int b = 5;
		System.out.println(max(a, b));
	}
}

在静态的主函数调用其他函数有以下两种方法:

  • static 修饰其他函数,可被主函数直接调用
  • 创建类的对象,通过类的对象间接地调用函数

6.2 可变参数

可变参数的声明方式为:

typeName... parameterName

举个例子:

public class Test{
    public static void main(String[] args){
        System.out.println(sum(1, 2, 3, 4, 5));  // 15.0
    }

    public static double sum(double... list){
        double res = 0;
        for (double elem : list){
            res += elem;
        }
        return res;
    }
}

此处传入的参数有很多个,可以像数组一样来访问传入的参数。


7. 数组

7.1 一维数组

数组的声明

可以用下面两种方法声明数组:dateType[] arrayName;dataType arrayName[];,但与 C++ 不同的是 Java 语言中声明数组的时候不可以指定数组长度。

int[] animals;
double[] salaries;
String[] names;

创建数组

所谓创建数组,就是要为数组分配内存空间,不分配内存是不能存放数组元素的,创建数组就是在内存中划分出几个连续的空间用于依次存储数组中的数据元素,其语法形式如下。

int[] myList;          // 声明数组
myList = new int[10];  // 创建数组

String[] names = new String[6];   // 声明和创建

上面的代码创建数组的过程为:在堆内存中创建 10 个连续的、存放 int 型元素的空间,并把存储空间的首地址赋给变量 myList。

元素访问

可以使用下标 [] 访问数组元素,可以使用冒号 : 循环数组元素:

当使用数组作函数的参数时,采用的是引用传递,可以直接对内存数据进行修改。

int[] numbers = new int[5];

for (int i=0; i<numbers.length; i++){
    numbers[i] = i;
}

for (int number : numbers){
    System.out.println(number);
}

数组初始化

String[] names = {"Tom", "Lucy", "Jerrey"};

7.2 二维数组

public class Test{

    public static void main(String[] args){

        int[][] array = new int[3][4];

        for (int i=0; i<3; i++){
            for (int j=0; j<4; j++){
                array[i][j] = 10 * i + j;
                System.out.print(array[i][j]);
            }
            System.out.println();
        }
    }
}

8. String 字符串

8.1 String 类

String 类有以下构造函数:

  • String(String s):初始化一个新创建的String对象,使其表示一个与参数相同的字符序列。
  • String(char[] value):创建一个新的String对象,使其表示字符数组参数中当前包含的字符序列。
  • String(char[] value, int offset, int count):创建一个新的String对象,它包含取自字符数组参数的一个子数组的字符序列。

举个例子:

String str1 = "hello";
String str2 = new String("hello");
String str3 = new String("Hello world", 0, 5);

注意:String 字符串是常量,使用 String 创建的字符串不支持字符串内字符的操作

字符串的存储

当创建两个字符串变量,若字符串的内容相同,使用 new 创建得到的两个字符串地址不同,不使用 new 创建的两个字符串变量地址相同:

public class CharacterClass {

	public static void main(String[] args) {

		String str1 = "12345";	// 公共池
		String str2 = "12345";

		System.out.println(System.identityHashCode(str1));   // 打印地址对应的 hash
		System.out.println(System.identityHashCode(str2));

		String str3 = new String("12345");	// 堆
		String str4 = new String("12345");

		System.out.println(System.identityHashCode(str3));
		System.out.println(System.identityHashCode(str4));
	}
}
110718392    // str1 和 str2 是同一个字符串
110718392
231685785    // str3 和 str4 不是一个字符串
114935352

8.2 String 类方法

常用的字符串函数有:length(), equal(), charAt(), indexOf(), subString(), format()

public class Test{
    public static void main(String[] args){

        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1 == str2);      // false
        System.out.println(str1.equals(str2)); // true

        String s1 = new String("abcdefg");
        System.out.println(s1.charAt(2));      // c
        System.out.println(s1.indexOf('e'));   // 4

        String s2 = new String("This is a text.");
        System.out.println(s2.length());       // 15
        System.out.println(s2.substring(5,14));

        String formated_str = String.format("PI = %f", 3.14);
        System.out.println(formated_str);      // PI = 3.14
    }
}

注意equals 比较的是内容是否相等、== 比较的是引用的变量地址是否相等。

8.3 StringBuffer 类

 以下是 StringBuffer 类最常用的构造方法。

  • StringBuffer():构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符。
  • StringBuffer(String str):构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
  • StringBuffer(int capacity):构造一个字符串缓冲区,大小为 capacity 个字节。
  • StringBuffer 字符串使用场合为经常需要对字符串内容进行修改操作的场合。

常用方法如下:

public int length()
public char charAt(int index)
public void setCharAt(int index, char ch)
public StringBuffer deleteCharAt(int index)

public StringBuffer append(char[] str)            // 追加字符串
public StringBuffer insert(int index, char[] str) // 在 index 插入 str
public StringBuffer delete(int start, int end)    // 删除 [start, end)
public StringBuffer replace(int start, int end, String str)

public String substring(int beginIndex, int endIndex)
public synchronized StringBuffer reverse()

举个例子:

public class Test{
    public static void main(String[] args){
    	StringBuffer sb = new StringBuffer("this is a StringBuffer");
    	System.out.println(sb);

    	sb.setCharAt(0, 'T');
    	System.out.println(sb);

    	sb.append(".");
    	System.out.println(sb);

    	sb.insert(8, "not ");
    	System.out.println(sb);

    	sb.delete(8, 12);
    	System.out.println(sb);
    }
}

输出结果为:

this is a StringBuffer
This is a StringBuffer
This is a StringBuffer.
This is not a StringBuffer.
This is a StringBuffer.

StringBuilder 与 StringBuffer 使用方法类似,虽然 StringBuilder 不是线程安全的,但速度快,因此大多数情况下用 StringBuilder 类。

8.4 正则表达式

可以使用正则表达式来判断一个字符串是否满足特定的规则,如:使用 String 类的 matches 函数来匹配字符串:

public class Test{

	public static boolean isValidMobileNumber(String s) {
		return s.matches("\\d{11}");
	}

	public static boolean consistsOfLetters(String s) {
		String regex = "[a-zA-Z]+";
		return s.matches(regex);
	}

    public static void main(String[] args){
    	String tel = new String("13184542126");
    	System.out.println(isValidMobileNumber(tel));
    	String word = new String("hahhahicajsdl");
    	System.out.println(consistsOfLetters(word));
    }
}

上面的例子中,定义了一个模板字符串 regex ,之后调用待测字符串的 matches 函数来进行匹配判断。(注意当使用了 \时需要转义,即使用 \\

常见的匹配规则如下:

匹配开头结尾:^$

匹配任意字符:.

匹配数字:\d

匹配空格:\s

匹配非数字:\D

匹配有或无:str?

匹配重复N次:\d{6} 连续的6个数字

重复匹配一个以上: +,如一个以上的连续数字\d+

重复匹配零个以上:*

匹配特定范围:[a-z]——匹配小写字母、[a-zA-Z]——匹配字母;[1-5]——匹配数字 1-5

匹配特定范围外:[^a-z]——匹配非小写字母

或规则匹配:|,如 AB|CD——匹配 AB 或 匹配 CD

补充:对于复杂的表达式,可以使用括号 () 括起来


9. 读写操作

9.1 Scanner 类

Java 提供了 Scannner 类,可以设置参数来从键盘读取数据,创建Scanner对象如下:

import java.util.Scanner;
Scanner scan = new Scanner(System.in); // 参数 System.in 代表读取键盘的输入

9.2 读取字符串

输入字符串的方式有两种:

next()

  • 一定要读取到有效字符后才可以结束输入
  • 输入一行时,以空格分隔字符串,因此不能得到带有空格的字符串;

nextLine()

  • 输入一行,以换行符结束,能读取有空格的字符串
  • 可以读取空白内容

举个例子:(输入为 “hello world”)

import java.util.Scanner;

public class Test{
    public static void main(String[] args){

        Scanner sc1 = new Scanner(System.in);   
        System.out.println(sc1.next());         // hello

        Scanner sc2 = new Scanner(System.in);
        System.out.println(sc2.nextLine());     // hello world

    }
}

9.3 读取数字

下面的程序对输入的所有数字求和,当输入不是 double 类型时(如字符串),程序会终止

import java.util.Scanner;

public class Test {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		double sum = 0;
		while (sc.hasNextDouble()) {
			sum += sc.nextDouble();
		}
		sc.close();
		System.out.println("Sum: " + sum);
	}
}

9.4 文件读写操作

可以使用 BufferedWriterBufferedReader 实现按行读写操作:

9.4.1 创建对象

构造函数声明如下:

public BufferedReader(Reader in)
public BufferedWriter(Writer out)

创建 BufferedWriter 对象:

import java.io.File;
import java.io.FileWriter;
import java.io.BufferedWriter;

File file = new File("a.txt");
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);

创建 BufferedReader 对象:

import java.io.FileReader;
import java.io.BufferedReader;

BufferedReader br = new BufferedReader(new FileReader("a.txt"));

9.4.2 常用方法

BufferedWriter 类常用的方法有:

void write(char ch)		// 写入单个字符
void write(char []cbuf,int off,int len)	// 写入字符数据的某一部分
void write(String s,int off,int len)	// 写入字符串的某一部分
void newLine()	// 写入一个行分隔符
void flush()	// 刷新缓冲,将缓冲数据写到目的文件中
void close()	// 关闭此流,再关闭前会先刷新

BufferedReader 类常用的方法有:

int read()				// 读取单个字符  达到尾部返回-1
boolean ready()    		// Tells whether this stream is ready to be read
String readLine();  	// Reads a line of text
void close();           // 关闭该流并释放与该流相关的所有资源

9.4.3 实例程序

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

public class Test{
    public static void main(String[] args){
    	try {
    		BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
    		bw.write("Line 1");
    		bw.newLine();
    		bw.write("Line 2");
    		bw.newLine();
    		bw.write("Line 3");
    		bw.flush();     // 刷新缓冲区,将缓冲区的内容写入到文件
    		bw.close();

    		System.out.println("Finish writting");

    		BufferedReader br = new BufferedReader(new FileReader("a.txt"));
    		while (br.ready()) {
    			System.out.println(br.readLine());
    		}
    		br.close();
    	}
    	catch (Exception e) {
			// TODO: handle exception
    		System.out.println(e);
		}
    }
}

输出结果如下:

Finish writting
Line 1
Line 2
Line 3

下面这个程序实现了文件的复制:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

public class Test{
    public static void main(String[] args){
    	try {
    		FileReader fr = new FileReader("a.txt");
    		FileWriter fw = new FileWriter("b.txt");
    		BufferedReader br = new BufferedReader(fr);
    		BufferedWriter bw = new BufferedWriter(fw);

    		while (br.ready()) {
    			bw.write(br.readLine());
    			bw.newLine();
    		}
    		bw.flush();
    		br.close();
    		bw.close();
    	}
    	catch (Exception e) {
			// TODO: handle exception
    		System.out.println(e);
		}
    }
}

运行之后会在工作文件夹 Test 下生成 b.txt,里面的内容和 a.txt 一样。


10. 异常捕获

使用 try-catch 来捕获 异常

try{
    double c = 3/0;
} catch (Exception e){
    System.out.println("Got an error: " + e); // 打印错误
} finally{
    System.out.println("This statement is excuted!");
}

可以有多个 catch 来捕获不同类型的错误;不论是否存在异常,finall关键字中的语句都会被执行。

输出结果为:

Got an error: java.lang.ArithmeticException: / by zero
This statement is excuted!

11. 时间和日期

11.1 查看时间

java.util 包提供了 Date 类来封装当前的日期和时间,可以 new 一个 Date 对象:

import java.util.Date;

public class Test{
    public static void main(String[] args){
        Date date = new Date();    // 初始化对象
        System.out.println(date.toString());
    }
}

11.2 格式化输出时间

import java.util.Date;
import java.text.SimpleDateFormat;

public class Test{
    public static void main(String[] args){
        // 创建 Date 对象
        Date currTime = new Date();
        // 设置格式
        SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss yyyy/MM/dd E");
        // 格式化输出
        System.out.println("Current time: " + ft.format(currentTime));
    }
}

11.3 测量时间

Thread.sleep(1000) 休眠 1000ms

System.currentTimeMillis() 获取毫秒数

import java.util.Date;

try {
    // 获取从1970年1月1号0时0分0秒至今的毫秒数
    long start = System.currentTimeMillis();
    System.out.println(new Date().toString());

    // 休眠 3 s
    Thread.sleep(3000);

    System.out.println(new Date().toString());
    long end = System.currentTimeMillis();

    long diff = start - end;
    System.out.println(diff + " msec");
} catch (Exception e) {
    System.out.println("Got an exception!");
}

11.4 Calendar 类

import java.util.Calendar;

public class Test1 {

	public static void main(String[] args) {
		Calendar c1 = Calendar.getInstance();
		System.out.println(c1.getTime());
		System.out.println(c1.get(Calendar.YEAR));
		System.out.println(c1.get(Calendar.MONTH));
		System.out.println(c1.get(Calendar.DATE));
	}
}

13. data structure

Java 提供的容器框架如下:

容器由 Collections继承而来,算法可以通过 Collections类来调用,容器的创建方法如下:

import java.util.ContainerName;    // 引入容器类

ContainerName<E> objectName = new ContainerName<E>();  // 初始化

13.1 ArrayList

ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制;

package dataStructure;

import java.util.ArrayList;
import java.util.Collections;

public class dataStructure {

	public static void main(String[] args) {

		ArrayList<Integer> myList = new ArrayList<Integer>();
		myList.add(1);
		myList.add(5);
		myList.add(2);
		myList.add(6);
		myList.add(4);
		myList.remove(0);
		for (Integer i : myList) {
			System.out.print(i + " ");
		}

		System.out.println();
		Collections.sort(myList);
//		Collections.reverse(myList);
		for (Integer i : myList) {
			System.out.print(i + " ");
		}
	}
}

13.2 Stack

Stack 常用的方法有:boolean empty(), E push(), E pop(), E peek()

package dataStructure;

import java.util.Stack;

public class dataStructure {

	public static void main(String[] args) {
		Stack<Integer> s = new Stack<Integer>();
		s.push(1);              // 压栈
		s.push(2);
		s.push(3);
		s.push(4);
		s.push(5);

		Integer a = s.peek();   // 访问栈顶
		System.out.println(a);

		Integer sum = 0;
		while(!s.empty()) {     // 判空
			sum += s.pop();     // 弹栈
		}
		System.out.println(sum);
	}
}

除此之外,Java 还有 Vector, LinkList, Queue, Dictionary等常用容器,可以通过查看手册或查看源码来学习使用!


以上就是目前学习的内容了,笔记持续更新中~

-->