【java学习-1】命名规则 / 注释 / 数据类型 / 运算 / 字符串 / 数组 / 输入输出 / if / switch / for / 命令行参数

1 Java快速入门

1.1 java简介

  Java介于编译型语言和解释型语言之间。编译型语言如C、C++,代码是直接编译成机器码执行,但是不同的平台(x86、ARM等)CPU的指令集不同,因此,需要编译出每一种平台的对应机器码。解释型语言如Python、Ruby没有这个问题,可以由解释器直接加载源码然后运行,代价是运行效率太低。而Java是将代码编译成一种“字节码”,它类似于抽象的CPU指令,然后,针对不同平台编写虚拟机,不同平台的虚拟机负责加载字节码并执行,这样就实现了“一次编写,到处运行”的效果。当然,这是针对Java开发者而言。对于虚拟机,需要为每个平台分别开发。为了保证不同平台、不同公司开发的虚拟机都能正确执行Java字节码,SUN公司制定了一系列的Java虚拟机规范。从实践的角度看,JVM的兼容性做得非常好,低版本的Java字节码完全可以正常运行在高版本的JVM上。


  JDK:Java Development Kit,Java开发工具包,包含JRE
  JRE:Java Runtime Environment,Java运行时环境,包含JVM
  JVM:Java Virtual Machine,Java虚拟机
  JDK开发Java程序,JRE允许Java程序
  JDK=JRE+开发工具
  JRE=JVM+核心类库


  细心的童鞋还可以在JAVA_HOME的bin目录下找到很多可执行文件:

  java:这个可执行程序其实就是JVM,运行Java程序,就是启动JVM,然后让JVM执行指定的编译后的代码;
  javac:这是Java的编译器,它用于把Java源码文件(以.java后缀结尾)编译为Java字节码文件(以.class后缀结尾);
  jar:用于把一组.class文件打包成一个.jar文件,便于发布;
  javadoc:用于从Java源码中自动提取注释并生成文档;
  jdb:Java调试器,用于开发阶段的运行调试。

1.2 第一个Java程序

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

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

  当我们把代码保存为文件时,文件名必须是Hello.java,而且文件名也要注意大小写,因为要和我们定义的类名Hello完全保持一致。


  Java源码本质上是一个文本文件,我们需要先用javacHello.java编译成字节码文件Hello.class,然后,用java命令执行这个字节码文件。其中可执行文件javac是编译器,而可执行文件java就是虚拟机。

在这里插入图片描述

1.3 Java的类方法命名规则、注释

  1. 习惯用大写英文字母开头,例如NoteBook
  2. 方法习惯用小写英文字母开头,例如goodMorning
  3. Public为访问修饰符,static也是修饰符

/**
 * 可以用来自动创建文档的注释
 * @auther xiaohua
 */
public class Hello { //一个类
    public static void main(String[] args) {  //一个方法
        System.out.println("Hello, world!");
    }
}
/*
* 另一种注释
*/

1.4 Java基本数据类型

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

  计算机内存的最小存储单元是字节(byte),一个字节就是一个8位二进制数,即8个bit。它的二进制表示范围从00000000-11111111,换算成十进制是0-255,换算成十六进制是00~ff。
在这里插入图片描述


整型

  对于整型类型,Java只定义了带符号的整型,因此,最高位的bit表示符号位(0表示正数,1表示负数)。各种整型能表示的最大范围如下:

byte-128 ~ 127
short: -32768 ~ 32767
int: -2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807
public class Main {
    public static void main(String[] args) {
        int i = 2147483647;
        int i2 = -2147483648;
        int i3 = 2_000_000_000; // 加下划线更容易识别
        int i4 = 0xff0000; // 十六进制表示的16711680
        int i5 = 0b1000000000; // 二进制表示的512
        long l = 9000000000000000000L; // long型的结尾需要加L
    }
}

浮点型

float f1 = 3.14f; //对于float类型,需要加上f后缀。
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 科学计数法表示的4.9x10^-324

布尔类型

boolean b1 = true;
boolean b2 = false;
boolean isGreater = 5 > 3; // 计算结果为true
int age = 12;
boolean isAdult = age >= 18; // 计算结果为false

字符类型

  字符类型char表示一个字符。Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符:

public class Main {
    public static void main(String[] args) {
        char a = 'A';
        char zh = '中';
        System.out.println(a);
        System.out.println(zh);
    }
}

引用类型

  除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是String字符串:String s = "hello";
  引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置,后续我们介绍类的概念时会详细讨论。

常量

定义变量的时候,如果加上final修饰符,这个变量就变成了常量:

final double PI = 3.14; // PI是一个常量
double r = 5.0;
double area = PI * r * r;
PI = 300; // 这是错误的,因为常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。

常量的作用是用有意义的变量名来避免魔术数字(Magic number),例如,不要在代码中到处写3.14,而是定义一个常量。如果将来需要提高计算精度,我们只需要在常量的定义处修改,例如,改成3.1416,而不必在所有地方替换3.14。

根据习惯,常量名通常全部大写。

var关键字

有些时候,类型的名字太长,写起来比较麻烦。例如:

StringBuilder sb = new StringBuilder();

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

var sb = new StringBuilder();

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

var sb = new StringBuilder();

实际上会自动变成:

StringBuilder sb = new StringBuilder();

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

1.5 整数运算

移位运算

对byte和short类型进行移位时,会首先转换为int再进行位移。
仔细观察可发现,左移实际上就是不断地×2,右移实际上就是不断地÷2。

int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n << 1;  // 00000000 00000000 00000000 00001110 = 14
int b = n << 2;  // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912
int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1;  // 00000000 00000000 00000000 00000011 = 3
int b = n >> 2;  // 00000000 00000000 00000000 00000001 = 1
int c = n >> 3;  // 00000000 00000000 00000000 00000000 = 0
int n = -536870912;
int a = n >> 1;  // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2;  // 11111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1

还有一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的1变成了0:

int n = -536870912;
int a = n >>> 1;  // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2;  // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1

位运算

位运算是按位进行与、或、非和异或的运算。
与运算的规则是,必须两个数同时为1,结果才为1:
或运算的规则是,只要任意一个为1,结果就为1:
非运算的规则是,0和1互换:
异或运算的规则是,如果两个数不同,结果为1,否则为0:

public class Main {
    public static void main(String[] args) {
        int i = 167776589; // 00001010 00000000 00010001 01001101
        int n = 167776512; // 00001010 00000000 00010001 00000000
        System.out.println(i & n); // 167776512
    }
}

运算优先级

在这里插入图片描述

类型自动提升与强制转型

在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。例如,short和int计算,结果总是int,原因是short首先自动被转型为int
也可以将结果强制转型,即将大范围的整数转型为小范围的整数。强制转型使用(类型),例如,将int强制转型为short:

int i = 12345;
short s = (short) i; // 12345

要注意,超出范围的强制转型会得到错误的结果,原因是转型时,int的两个高位字节直接被扔掉,仅保留了低位的两个字节:

public class Main {
    public static void main(String[] args) {
        int i1 = 1234567;
        short s1 = (short) i1; // -10617
        System.out.println(s1);
        int i2 = 12345678;
        short s2 = (short) i2; // 24910
        System.out.println(s2);
    }
}

1.6 浮点数运算

  浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。
  在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示。
  举个栗子:
  浮点数0.1在计算机中就无法精确表示,因为十进制的0.1换算成二进制是一个无限循环小数,很显然,无论使用float还是double,都只能存储一个0.1的近似值。但是,0.5这个浮点数又可以精确地表示。
  因为浮点数常常无法精确表示,因此,浮点数运算会产生误差。

  判断浮点数是否相等:由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。正确的比较方法是判断两个浮点数之差的绝对值是否小于一个很小的数
  类型提升:如果参与运算的两个数其中一个是整型,那么整型可以自动提升到浮点型。
  溢出:整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值:
  NaN表示Not a Number
  Infinity表示无穷大
  -Infinity表示负无穷大
  强制转型:可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。例如:

int n1 = (int) 12.3; // 12
int n2 = (int) 12.7; // 12
int n2 = (int) -12.7; // -12
int n3 = (int) (12.7 + 0.5); // 13
int n4 = (int) 1.2e20; // 2147483647

如果要进行四舍五入,可以对浮点数加上0.5再强制转型。 int n = (int) (d + 0.5);

1.7 布尔运算

布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。

因为false && x的结果总是false,无论x是true还是false,因此,与运算在确定第一个值为false后,不再继续计算,而是直接返回false

1.8 字符和字符串

Java的字符类型char是基本类型,字符串类型String是引用类型;
基本类型的变量是“持有”某个数值,引用类型的变量是“指向”某个对象;
引用类型的变量可以是空值null;

1.8.1 字符类型

字符类型char是基本数据类型。一个char保存一个Unicode字符。要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可。还可以直接用转义字符\u+Unicode编码来表示一个字符:

// char
char c1 = 'A';
char c2 = '中';
// Unicode编码
int n1 = 'A'; // 字母“A”的Unicodde编码是65
int n2 = '中'; // 汉字“中”的Unicode编码是20013
// 转义字符 注意是十六进制:
char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65
char c4 = '\u4e2d'; // '中',因为十六进制4e2d = 十进制20013

1.8.2 字符串类型

和char类型不同,字符串类型String是引用类型

String s = ""; // 空字符串,包含0个字符
String s3 = "中文 ABC"; // 包含6个字符,其中有一个空格
String s = "ABC\n\u4e2d\u6587"; // 包含6个字符: A, B, C, 换行符, 中, 文

常见的转义字符包括:

\" 表示字符"
\' 表示字符'
\\ 表示字符\
\n 表示换行符
\r 表示回车符
\t 表示Tab
\u#### 表示一个Unicode编码的字符

可以使用+连接任意字符串和其他数据类型。
从Java 13开始,字符串可以用"""..."""表示多行字符串(Text Blocks)。

1.8.3 不可变特性

Java的字符串除了是一个引用类型外,还有个重要特点,就是字符串不可变。考察以下代码:

public class Main {
    public static void main(String[] args) {
        String s = "hello";
        System.out.println(s); // 显示 hello
        s = "world";
        System.out.println(s); // 显示 world
    }
}

在这里插入图片描述

1.9 数组

定义一个数组类型的变量,使用数组类型“类型[]”,例如,int[]。和单个基本类型变量不同,数组变量初始化必须使用new int[5]表示创建一个可容纳5个int元素的数组。

Java的数组有几个特点:
▲数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;
▲数组一旦创建后,大小就不可改变。
▲数组是引用类型

public class Main {
    public static void main(String[] args) {
        // 5位同学的成绩:
        int[] ns = new int[5];
        ns[0] = 68;
        ns[1] = 79;
        ns[2] = 91;
        ns[3] = 85;
        ns[4] = 62;
        //法2:
        int[] ns = new int[] { 68, 79, 91, 85, 62 };
        //法3:
        int[] ns = { 68, 79, 91, 85, 62 };

    }
}

注意数组是引用类型,并且数组大小不可变。我们观察下面的代码:

public class Main {
    public static void main(String[] args) {
        // 5位同学的成绩:
        int[] ns;
        ns = new int[] { 68, 79, 91, 85, 62 };
        System.out.println(ns.length); // 5
        ns = new int[] { 1, 2, 3 };
        System.out.println(ns.length); // 3
    }
}

数组大小变了吗?看上去好像是变了,但其实根本没变。

对于数组ns来说,执行ns = new int[] { 68, 79, 91, 85, 62 };时,它指向一个5个元素的数组:
在这里插入图片描述

执行ns = new int[] { 1, 2, 3 };时,它指向一个新的3个元素的数组:
在这里插入图片描述

但是,原有的5个元素的数组并没有改变,只是无法通过变量ns引用到它们而已。

字符串数组
如果数组元素不是基本类型,而是一个引用类型,那么,修改数组元素会有哪些不同?

字符串是引用类型,因此我们先定义一个字符串数组:

String[] names = {
    "ABC", "XYZ", "zoo"
};

对于String[]类型的数组变量names,它实际上包含3个元素,但每个元素都指向某个字符串对象:
在这里插入图片描述

对names[1]进行赋值,例如names[1] = “cat”;,效果如下:
在这里插入图片描述

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


public class Main {
    public static void main(String[] args) {
        String[] names = {"ABC", "XYZ", "zoo"};
        String s = names[1];
        names[1] = "cat";
        System.out.println(s); // s是"XYZ"
    }
}
public class Main {
    public static void main(String[] args) {
        String[] names = {"ABC", "XYZ", "zoo"};
        String[] s = names;
        names[1] = "cat";
        System.out.println(s[1]); // s是"cat"
    }
}

1.10 输出

double d = 3.1415926;
System.out.printf("%.2f\n", d); // 显示两位小数3.14
System.out.printf("%.4f\n", d); // 显示4位小数3.1416

Java的格式化功能提供了多种占位符,可以把各种数据类型“格式化”成指定的字符串:(注意,由于%表示占位符,因此,连续两个%%表示一个%字符本身。)
%d 格式化输出整数
%x 格式化输出十六进制整数
%f 格式化输出浮点数
%e 格式化输出科学计数法表示的浮点数
%s 格式化字符串

占位符本身还可以有更详细的格式化参数。下面的例子把一个整数格式化成十六进制,并用0补足8位:

int n = 12345000;
System.out.printf("n=%d, hex=%08x", n, n); //n=12345000, hex=00bc5ea8

1.11 输入

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in); // 创建Scanner对象
        String name = scanner.nextLine(); // 读取一行输入并获取字符串
        int age = scanner.nextInt(); // 读取一行输入并获取整数
        System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出
    }
}

通过import语句导入java.util.Scanner,创建Scanner对象并传入System.in。
System.out代表标准输出流,而System.in代表标准输入流。

Scanner是一个,nextLine()是Scanner的成员函数,System.in作为参数传递给Scanner的构造函数,使Scanner用键盘作为输入,然后用new在内存中实例化一个Scanner出来,使得其它变量能调用这块内存区。

1.12 if判断

在Java中,判断类型的变量是否相等,可以使用 = =运算符。
但是,判断引用类型的变量是否相等,= =表示“引用是否相等”,或者说,是否指向同一个对象。例如,下面的两个String类型,它们的内容是相同的,但是,分别指向不同的对象,用==判断,结果为false。要判断引用类型的变量内容是否相等,必须使用equals()方法。

public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO".toLowerCase();
        System.out.println(s1);
        System.out.println(s2);
        if (s1 == s2) {
            System.out.println("s1 == s2"); //Fasle
        } 
		if (s1.equals(s2)) {
            System.out.println("s1 equals s2");  //True
        }
    }
}

注意:执行语句s1.equals(s2)时,如果变量s1为null,会报NullPointerException。要避免NullPointerException错误,可以把一定不是null的对象"hello"放到前面:例如:if ("hello".equals(s)) { ... }。也可以利用短路运算符&&:

public class Main {
    public static void main(String[] args) {
        String s1 = null;
        if (s1 != null && s1.equals("hello")) {
            System.out.println("hello");
        }
    }
}

1.13 switch判断

public class Main {
    public static void main(String[] args) {
        String fruit = "apple";
        switch (fruit) {
        case "apple":
            System.out.println("Selected apple");
            break;
        case "pear":
            System.out.println("Selected pear");
            break;
        case "mango":
            System.out.println("Selected mango");
            break;
        default:
            System.out.println("No fruit selected");
            break;
        }
    }
}

从Java 12开始,switch语句升级为更简洁的表达式语法,使用类似模式匹配(Pattern Matching)的方法,保证只有一种路径会被执行,并且不需要break语句。

public class Main {
    public static void main(String[] args) {
        String fruit = "apple";
        switch (fruit) {
        case "apple" -> System.out.println("Selected apple");
        case "pear, banana" -> System.out.println("Selected pear or banana");
        case "mango" -> {
            System.out.println("Selected mango");
            System.out.println("Good choice!");
        }
        default -> System.out.println("No fruit selected");
        }
    }
}

使用新的switch语法,不但不需要break,还可以直接返回值。

public class Main {
    public static void main(String[] args) {
        String fruit = "apple";
        int opt = switch (fruit) {
            case "apple" -> 1;
            case "pear", "mango" -> 2;
            default -> 0;
        }; // 注意赋值语句要以;结束
        System.out.println("opt = " + opt);
    }
}

但是,如果需要复杂的语句,我们也可以写很多语句,放到{…}里,然后,用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);
    }
}

1.14 for循环

1.14.1 for循环

for循环的功能非常强大,它使用计数器实现循环。for循环会先初始化计数器,然后,在每次循环前检测循环条件,在每次循环后更新计数器。计数器变量通常命名为i。
在for循环执行前,会先执行初始化语句int i=1,它定义了计数器变量i并赋初始值为1,然后,循环前先检查循环条件i<=100,循环后自动执行i++,因此,和while循环相比,for循环把更新计数器的代码统一放到了一起。在for循环的循环体内部,不需要去更新变量i。

1.14.2 for each循环

更简单地遍历数组。除了数组外,for each循环能够遍历所有“可迭代”的数据类型,包括后面会介绍的List、Map等。

int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
    System.out.println(n);

1.15 命令行参数

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

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

public class Main {
    public static void main(String[] args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

我们可以利用接收到的命令行参数,根据不同的参数执行不同的代码。例如,实现一个-version参数,打印程序版本号:

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

上面这个程序必须在命令行执行,我们先编译它:

$ javac Main.java

然后,执行的时候,给它传递一个-version参数:

$ java Main -version
v 1.0

这样,程序就可以根据传入的命令行参数,作出不同的响应。

小结
命令行参数类型是String[]数组;

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

如何解析命令行参数需要由程序自己实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值