Java学习(1)

Java随想写(一)

Hello, Java!

此部分内容学习来自:Java教程 - 廖雪峰的官方网站 (liaoxuefeng.com)

必要简介
  • Java最早是由SUN公司(已被Oracle收购)的詹姆斯·高斯林(高司令,人称Java之父)在上个世纪90年代初开发的一种编程语言,最初被命名为Oak,目标是针对小型家电设备的嵌入式应用

  • Java介于编译型语言和解释型语言之间。编译型语言如C、C++,代码是直接编译成机器码执行,但是不同的平台(x86、ARM等)CPU的指令集不同,因此,需要编译出每一种平台的对应机器码。

  • Java的三个版本与关系:

    • Java SE:Standard Edition
    • Java EE:Enterprise Edition
    • Java ME:Micro Edition
  • 相关名词:

    • JDK:Java Development Kit

    • JRE:Java Runtime Environment

    • JSR规范:Java Specification Request

    • JCP组织:Java Community Process

    • RI:Reference Implementation

    • TCK:Technology Compatibility Kit

编译方式

java源码本质上是一个文本文件,我们需要先用javacHello.java编译成字节码文件Hello.class,然后,用java命令执行这个字节码文件:

因此,可执行文件 javac是编译器,而可执行文件java就是虚拟机。

  • 在不使用外部IDE情况下编译:

    第一步,在保存Hello.java的目录下执行命令javac Hello.java

    $ javac Hello.java
    

    如果源代码无误,上述命令不会有任何输出,而当前目录下会产生一个Hello.class文件:

    $ ls
    Hello.class	Hello.java
    

    第二步,执行Hello.class,使用命令java Hello

    $ java Hello
    Hello, world!
    

    注意:给虚拟机传递的参数Hello是我们定义的类名,虚拟机自动查找对应的class文件并执行。

  • Java 11新增功能:直接运行一个单文件源码:

    $ java Hello.java sh
    Hello, world!
    

    需要注意的是,在实际项目中,单个不依赖第三方库的Java源码是非常罕见的,所以,绝大多数情况下,我们无法直接运行一个Java源码文件,原因是它需要依赖其他的库。

注意点

一个Java源码只能定义一个public类型的class,并且class名称和文件名要完全一致;

练习使用代码
  • Hello word!
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

Dismember Java!
与C/C++同而不同
基本单位:类(class)

Java是面向对象的语言,一个程序的基本单位就是classclass是关键字,这里定义的class名字就是Hello

public class Hello { // 类名是Hello
    // ...
} // class定义结束

类名要求:

  • 类名必须以英文字母开头,后接字母,数字和下划线的组合
  • 习惯以大写字母开头
类的嵌套:方法(method)

class内部,可以定义若干方法(method):

/** 特殊注释
 * 可以用来自动创建文档的注释
 * 
 * @auther liaoxuefeng
 */
public class Hello {
    public static void main(String[] args) { // 方法名是main
        // 方法代码...
        System.out.println("Hello, world!");	//;结尾
    } // 方法定义结束
}

方法定义了一组执行语句,方法内部的代码将会被依次顺序执行

这里的方法名是main,返回值是void,表示没有任何返回值。

我们注意到public除了可以修饰class外,也可以修饰方法。而关键字static是另一个修饰符,它表示静态方法,后面我们会讲解方法的类型,目前,我们只需要知道,Java入口程序规定的方法必须是静态方法方法名必须为main,括号内的参数必须是String数组,习惯以小写字母开头且只含字母。

有一种特殊的多行注释,以/**开头,以*/结束,如果有多行,每行通常以星号开头

数据类型

Java定义了以下几种基本数据类型:

  • 整数类型:byte,short,int,long
  • 浮点数类型:float,double
  • 字符类型:char
  • 布尔类型:boolean

整数类型

溢出:要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果(二进制最高位0变为1,成为负数)

移位/位运算/自增/自减:java提供了C/C++相同的特殊运算,增加了一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0

浮点数类型

自动转换:一个例子将会帮你理解其优先性double d = 1.2 + 24 / 5; // 5.2

溢出:整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值:

  • NaN表示Not a Number
  • Infinity表示无穷大
  • -Infinity表示负无穷大

字符串类型

多行字符串:从Java 13开始,字符串可以用"""..."""表示多行字符串(Text Blocks)

不可变特性:字符串String是引用类型,字符串内容不会改变,但是会改变变量指向空间

空值:java中的引用变量可以指向空值null,类似于C++的空指针,但是null ≠ \neq =""

需要注意的是,在java中字符(char)与字符串(String)是两个不同数据类型

var关键字

如果想省略变量类型,可以使用var关键字:

var sb = new StringBuilder();

编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder。对编译器来说,语句:

var sb = new StringBuilder();

实际上会自动变成:

StringBuilder sb = new StringBuilder();

因此,使用var定义变量,仅仅是少写了变量类型而已。

数组类型

声明数组类型方式:

public class Hello {
    public static void main(String[] avg) {
        int[] arr = new int[5];
        arr[0] = 68;
        System.out.println(arr[1]);   //0
        System.out.println(arr.length); //5
        
        //下述为 int[] ns = new int[] { 68, 79, 91, 85, 62 }; 的缩写
        int[] ns = { 68, 79, 91, 85, 62 };
    }
}

我们通过上述代码可以发现一些有意思的事情:

  1. Java所有元素都有初始化的默认值,值得提醒的是Boolean默认是false
  2. 与C++库类似地,有许多初始方法是可用的
  3. 因为此类型也是引用,因此arr = {1, 2, 3}其实是指向空间的变更

根据上述内容,我们会发现一个神奇的数据类型——字符串数组

对于String[]类型的数组变量names,它实际上包含3个元素,但每个元素都指向某个字符串对象:

          ┌─────────────────────────┐
    names │   ┌─────────────────────┼───────────┐
      │   │   │                     │           │
      ▼   │   │                     ▼           ▼
┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┐
│   │░░░│░░░│░░░│   │ "ABC" │   │ "XYZ" │   │ "zoo" │   │
└───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┘
      │                 ▲
      └─────────────────┘

names[1]进行赋值,例如names[1] = "cat";,效果如下:

          ┌─────────────────────────────────────────────────┐
    names │   ┌─────────────────────────────────┐           │
      │   │   │                                 │           │
      ▼   │   │                                 ▼           ▼
┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┬───────┬───┐
│   │░░░│░░░│░░░│   │ "ABC" │   │ "XYZ" │   │ "zoo" │   │ "cat" │   │
└───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┴───────┴───┘
      │                 ▲
      └─────────────────┘

这里注意到原来names[1]指向的字符串"XYZ"并没有改变,仅仅是将names[1]的引用从指向"XYZ"改成了指向"cat",其结果是字符串"XYZ"再也无法通过names[1]访问到了。

来尝试一段代码:

public class Hello {
    public static void main(String[] avg) {
        String[] strings = {"abc", "xyz", "zoo"};
        var s = strings[1];
        strings[1] = "cat";
        System.out.println(s);  //xyz
    }
}

说明存储"xyz"的空间仍然存在

练习使用的代码
  • 方程求根
public class Hello {
    public static void main(String[] args) {
        double a = 1.0;
        double b = 3.0;
        double c = -4.0;

        double r1 = (-b + Math.sqrt(b * b - 4 * a * c)) / 2 * a;
        double r2 = (-b - Math.sqrt(b * b - 4 * a * c)) / 2 * a;
        System.out.printf("x = %.2f, %.2f", r1, r2);
    }
}
  • Unicode值拼接字符串

这里的问题是挺有意思的,拼接方式不同得到的字符串是完全不同的,但是一般应该是第二种方式正确

public class Hello {
    public static void main(String[] args) {
        int a = 72;
        int b = 105;
        int c = 65281;

        //第一种,先计算成和再转换成字符串
        String s1 = (char) a + (char) b + (char) c + ""; //65458
        System.out.println(s1);

        String s4 = String.valueOf((char)a + (char)b + (char)c);
        System.out.println(s4); //65458

        //第二种,直接求得各个字符再相加
        String s2 = "" + (char) a + (char) b + (char) c;
        System.out.println(s2); //Hi!

        String s5 = String.valueOf((char)a) + String.valueOf((char) b) + String.valueOf((char) c) ;// Hi!
        System.out.println(s5); //Hi!

        //第三种,直接拼接各个字符串
        String s3 = "" + a + b + c;
        System.out.println(s3); //7210565281
    }
}

Step in Java!

此部分仍然倾向于从与C/C++和Python的异同出发

输入与输出
输出

格式化输出:此部分与C中的printf()方法基本无区别,只不过需要注意12900000在输出时自动转换成1.29E7

详细的格式化参数请参考JDK文档java.util.Formatter

输入

我们以下面这段代码为例:

import java.util.Scanner;   //人工翻:引入java依赖中的扫描仪[输入类]

public class Hello {
    public static void main(String[] avg) {
        Scanner scanner = new Scanner(System.in);	//System.in为输入流
        System.out.print("Input your name: ");
        var name = scanner.nextLine();
        System.out.print("Input your age: ");
        var age = scanner.nextInt();
        System.out.println(name.getClass());	//class java.lang.String
    }
}

可以发现,其对于输入内容的引入其实与Python的方式很相似,而且引入方式更接近于C++的scanf()函数;

另外,此处提及一个方法Object.getClass(),目前所学内容中只能适用于StringArray类型上。

当要测试输入,我们不能在线运行它,因为输入必须从命令行读取,因此,需要走编译、执行的流程:

$ javac Main.java
条件判断
  • if-else与两门语言的区别也不算很大,但有几个必须注意的地方:

    • 浮点数在计算机中常常无法精确表示,并且计算可能出现误差,因此,判断浮点数相等用==判断不靠谱【利用差值小于某个临界值来判断会更好】

      if (x == 0.1){
          ...
      }
      
      //改变:
      if (Math.abs(x - 0.1) < 0.00001){
          ...
      }
      
    • 在Java中,判断引用类型的变量是否相等,可以使用==运算符。但其判断的是两变量指向空间是否相同【引用类型变量内容相等判断应当使用Object1.equals(Object2)方式】

    注意:如果变量Object1null,会报NullPointerException,因此最好先判断变量是否指空

  • switch语句也与C/C++区别不大

    • 相比起C/C++可以增加字符串的匹配

    • 而且自Java 12起,其将case语句的语法引入了->

      String fruit = "apple";
      switch (fruit) {
          case "apple" -> System.out.println("Selected apple");
          case "pear" -> System.out.println("Selected pear");
          case "mango" -> {
              System.out.println("Selected mango");
              System.out.println("Good choice!");
          }
          default -> System.out.println("No fruit selected");
      }
      

      新语句没有穿透,因此不需要break

    • y i e l d yield yield​:大多数时候,在switch表达式内部,我们会返回简单的值。

      但是,如果需要复杂的语句,我们也可以写很多语句,放到{...}里,然后,用yield返回一个值作为switch语句的返回值:

      public class Main {
          public static void main(String[] args) {
              String fruit = "orange";
              int opt = switch (fruit) {
                  case "apple" -> 1;
                  case "pear", "mango" -> 2;
                  default -> {
                      int code = fruit.hashCode();
                      yield code; // switch语句返回值
                  }
              };
              System.out.println("opt = " + opt);
          }
      }
      
循环语句

其它循环语句较另外两个语言没有变化

for循环语句

这里介绍for-each方式,其实这种方式在C++中也有:

public class Main {
    public static void main(String[] args) {
        int[] ns = { 1, 4, 9, 16, 25 };
        for (int n : ns) {
            System.out.println(n);
        }
    }
}

for循环相比,for each循环的变量n不再是计数器,而是直接对应到数组的每个元素。for each循环的写法也更简洁。但是,for each循环无法指定遍历顺序,也无法获取数组的索引。

数组操作

特殊的地方:

  • 可以直接使用Java标准库提供的Arrays.sort()进行排序:

    import java.util.Arrays;
    
    public class Main {
        public static void main(String[] args) {
            int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
            Arrays.sort(ns);
            System.out.println(Arrays.toString(ns));
        }
    }
    
  • 多维数组:

    //二维数组的定义方法
    public class Main {
        public static void main(String[] args) {
            int[][] ns = {
                { 1, 2, 3, 4 },
                { 5, 6, 7, 8 },
                { 9, 10, 11, 12 }
            };
            System.out.println(ns.length); // 3
        }
    }
    

    打印多维数组可以使用Arrays.deepToString()

命令行参数

Java程序的入口是main方法,而main方法可以接受一个命令行参数,它是一个String[]数组。

这个命令行参数由JVM接收用户输入并传给main方法:

public class Hello {
    public static void main(String[] avgs) {
        for (String arg : avgs) {
            if ("-version".equals(arg)){
                System.out.println("v 1.0");
                break;
            }
        }
    }
}

可以看到完成了命令行的参数传递

练习使用代码
  • 小明又有活儿了

    请帮小明同学设计一个程序,输入上次考试成绩(int)和本次考试成绩(int),然后输出成绩提高的百分比,保留两位小数位(例如,21.75%)。

import java.util.Scanner;

public class Hello {
    public static void main(String[] avg) {
        System.out.print("Input your past mark: ");
        var scan = new Scanner(System.in);
        var past = scan.nextFloat();
        System.out.print("Input your present mark: ");
        var now = scan.nextFloat();
        double per = now / past - 1;
        System.out.printf("Increase %.2f",per);
    }
}
  • 救救孩子

    请用if ... else编写一个程序,用于计算体质指数BMI,并打印结果。

    BMI = 体重(kg)除以身高(m)的平方

    BMI结果:

    • 过轻:低于18.5
    • 正常:18.5-25
    • 过重:25-28
    • 肥胖:28-32
    • 非常肥胖:高于32
import java.util.Scanner;

public class Hello {
    public static void main(String[] avg) {
        var scan = new Scanner(System.in);
        System.out.print("Input your weight: ");
        var weight = scan.nextFloat();
        System.out.print("Input your length: ");
        var len = scan.nextFloat();
        float BMI = weight / (len * len);
        if (BMI < 18.5) {
            System.out.print("Level 1");
        } else if (BMI < 25) {
            System.out.print("Level 2");
        } else if (BMI < 28) {
            System.out.print("Level 3");
        } else if (BMI <= 32) {
            System.out.print("Level 4");
        } else {
            System.out.print("Level 5");
        }
    }
}
  • 三位一体

    使用switch实现一个简单的石头、剪子、布游戏

import java.util.Random;
import java.util.Scanner;

public class Hello {
    public static void main(String[] avg) {
        var r = new Random();
        int ai_opt = r.nextInt(3);
        System.out.print("""
                    There are three opts:
                    1. Rock
                    2. Knife
                    3. Clothing
                    You can select one from them, input your option: 
                """);

        var scan = new Scanner(System.in);
        var man_opt = scan.nextInt();

        var res = ai_opt * 3 + man_opt;
        if (man_opt < 1 || man_opt > 3) {
            System.out.print("Error selection!");
            return;
        }
        switch (res) {
            case 1, 5, 9 -> {
                System.out.print("Fair");
            }
            case 2, 6, 7 -> {
                System.out.print("Fail");
            }
            default -> System.out.print("Win");
        }
    }
}
  • m到n(1)

    使用while计算从mn的和:

public class Hello {
    public static void main(String[] avg) {
        int m = 4, n = 100, res = 0;
        while (m < n) {
            res += m;
            m++;
        }
        System.out.print("res = " + res);
    }
}
  • m到n(2)

    使用do while循环计算从mn的和:

public class Hello {
    public static void main(String[] avg) {
        int m = 4, n = 100, res = 0;
        do {
            res += m;
            m++;
        } while (m < n);
        System.out.print("res = " + res);
    }
}
  • π \pi π的麦克劳林

    圆周率π可以使用公式计算:

    π 4 = 1 − 1 3 + 1 5 − 1 7 + . . . \frac{\pi}{4}=1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+... 4π=131+5171+...

    请利用for循环计算π:

public class Hello {
    public static void main(String[] avg) {
        double pi = 0, tmp = 1;
        for (int i = 0; i < 100; i++) {
            pi += 1 * 1.0 / (2 * i + 1) * tmp;
            tmp *= (-1);
        }
        pi *= 4;
        System.out.print("Pi = " + pi);
    }
}
  • 遍历数组(燃尽了)

    请按倒序遍历数组并打印每个元素:

public class Hello {
    public static void main(String[] avg) {
        int[] ns = {1, 4, 9, 16, 25};
        for (int i = ns.length - 1; i >= 0; i--)
            System.out.printf("%d ", ns[i]);
    }
}
  • 数组排序
//冒泡排序为例
public class Hello {
    public static void main(String[] avg) {
        int[] ns = {28, 12, 89, 73, 65, 18, 96, 50, 8, 36};
        for (int i = 0; i < ns.length - 1; i++) {
            for (int j = 0; j < ns.length - i - 1; j++) {
                if (ns[j] > ns[j + 1]) {
                    int tmp = ns[j];
                    ns[j] = ns[j + 1];
                    ns[j + 1] = tmp;
                }
            }
        }
        for (int i = 0; i < ns.length; i++) {
            System.out.printf("%d ", ns[i]);
        }
    }
}
  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值