• java基础之java程序基础(二)--变量和数据类型


    前言....

    本节我们将介绍Java程序的基础知识,包括:

    • Java程序基本结构

    • 变量和数据类型

    • 整数运算

    • 浮点数运算

    • 布尔运算

    • 字符和字符串

    • 数组类型

    java-basic

    Java程序基本结构

    我们先剖析一个完整的Java程序,它的基本结构是什么:

    **
     * 可以用来自动创建文档的注释
     */
    public class Hello {
        public static void main(String[] args) {
            // 向屏幕输出文本:
            System.out.println("Hello, world!");
            /* 多行注释开始
            注释内容
            注释结束 */
        }
    } // class定义结束

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

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

    类名要求

    • 类名必须以英文字母开头,后接字母,数字和下划线的组合
    • 习惯以大写字母开头

    要注意遵守命名习惯,好的类命名:

    • Hello
    • NoteBook
    • VRPlayer

    不好的类命名

    • hello
    • Good123
    • Note_Book
    • _World

    注意到public是访问修饰符,表示该class是公开的。

    不写public,也能正确编译,但是这个类将无法从命令行执行

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

    public class Hello {
        public static void main(String[] args) { // 方法名是main
            // 方法代码...
        } // 方法定义结束
    }

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

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

    方法名也有命名规则,命名和class一样,但是首字母小写:

    在方法内部,语句才是真正的执行代码。Java的每一行语句必须以分号结束:

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

    在Java程序中,注释是一种给人阅读的文本,不是程序的一部分,所以编译器会自动忽略注释。

    Java有3种注释,第一种是单行注释,以双斜线开头,直到这一行的结尾结束:

    // 这是注释...

    而多行注释以/*星号开头,以*/结束,可以有多行:

    /*
    这是注释
    blablabla...
    这也是注释
    */

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

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

    这种特殊的多行注释需要写在类和方法的定义处,可以用于自动创建文档。

    变量和数据类型

    变量

    什么是变量?

    变量就是初中数学的代数的概念,例如一个简单的方程,x,y都是变量:

    y=x^2+1y=x2+1

    在Java中,变量分为两种:基本类型的变量和引用类型的变量。

    我们先讨论基本类型的变量。

    在Java中,变量必须先定义后使用,在定义变量的时候,可以给它一个初始值。例如:

    int x = 1;

    上述语句定义了一个整型int类型的变量,名称为x,初始值为1

    来看一个完整的定义变量,然后打印变量值的例子:

    // 定义并打印变量
    public class Main {
        public static void main(String[] args) {
            int x = 100; // 定义int类型变量x,并赋予初始值100
            System.out.println(x); // 打印该变量的值
        }
    }
    100

    变量的一个重要特点是可以重新赋值。例如,对变量x,先赋值100,再赋值200,观察两次打印的结果:

    public class Main {
        public static void main(String[] args) {
            int x = 100; // 定义int类型变量x,并赋予初始值100
            System.out.println(x); // 打印该变量的值,观察是否为100
            x = 200; // 重新赋值为200
            System.out.println(x); // 打印该变量的值,观察是否为200
        }
    }
    100
    200

    注意到第一次定义变量x的时候,需要指定变量类型int,因此使用语句int x = 100;。而第二次重新赋值的时候,变量x已经存在了,不能再重复定义,因此不能指定变量类型int,必须使用语句x = 200;

    变量不但可以重新赋值,还可以赋值给其他变量。让我们来看一个例子:

    //变量之间赋值
    public
    class Main { public static void main(String[] args) { int n = 100; // 定义变量n,同时赋值为100 System.out.println("n = " + n); // 打印n的值 n = 200; // 变量n赋值为200 System.out.println("n = " + n); // 打印n的值 int x = n; // 变量x赋值为n(n的值为200,因此赋值后x的值也是200) System.out.println("x = " + x); // 打印x的值 x = x + 100; // 变量x赋值为x+100(x的值为200,因此赋值后x的值是200+100=300) System.out.println("x = " + x); // 打印x的值 System.out.println("n = " + n); // 再次打印n的值,n应该是200还是300? } }

    我们一行一行地分析代码执行流程:

    执行int n = 100;,该语句定义了变量n,同时赋值为100,因此,JVM在内存中为变量n分配一个“存储单元”,填入值100

          n
          │
          ▼
    ┌───┬───┬───┬───┬───┬───┬───┐
    │   │100│   │   │   │   │   │
    └───┴───┴───┴───┴───┴───┴───┘
    

    执行n = 200;时,JVM把200写入变量n的存储单元,因此,原有的值被覆盖,现在n的值为200

          n
          │
          ▼
    ┌───┬───┬───┬───┬───┬───┬───┐
    │   │200│   │   │   │   │   │
    └───┴───┴───┴───┴───┴───┴───┘
    

    执行int x = n;时,定义了一个新的变量x,同时对x赋值,因此,JVM需要新分配一个存储单元给变量x,并写入和变量n一样的值,结果是变量x的值也变为200

          n           x
          │           │
          ▼           ▼
    ┌───┬───┬───┬───┬───┬───┬───┐
    │   │200│   │   │200│   │   │
    └───┴───┴───┴───┴───┴───┴───┘
    

    执行x = x + 100;时,JVM首先计算等式右边的值x + 100,结果为300(因为此刻x的值为200),然后,将结果300写入x的存储单元,因此,变量x最终的值变为300

    
          n           x
          │           │
          ▼           ▼
    ┌───┬───┬───┬───┬───┬───┬───┐
    │   │200│   │   │300│   │   │
    └───┴───┴───┴───┴───┴───┴───┘
    

    可见,变量可以反复赋值。注意,等号=是赋值语句,不是数学意义上的相等,否则无法解释x = x + 100

    基本数据类型

    基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:

    • 整数类型:byte,short,int,long

    • 浮点数类型:float,double

    • 字符类型:char

    • 布尔类型:boolean

    Java定义的这些基本数据类型有什么区别呢?要了解这些区别,我们就必须简单了解一下计算机内存的基本结构。

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

    内存单元从0开始编号,称为内存地址。每个内存单元可以看作一间房间,内存地址就是门牌号。

      0   1   2   3   4   5   6  ...
    ┌───┬───┬───┬───┬───┬───┬───┐
    │   │   │   │   │   │   │   │...
    └───┴───┴───┴───┴───┴───┴───┘
    

    一个字节是1byte,1024字节是1K,1024K是1M,1024M是1G,1024G是1T。一个拥有4T内存的计算机的字节数量就是:

    4T = 4 x 1024G
       = 4 x 1024 x 1024M
       = 4 x 1024 x 1024 x 1024K
       = 4 x 1024 x 1024 x 1024 x 1024
       = 4398046511104

    不同的数据类型占用的字节数不一样。我们看一下Java基本数据类型占用的字节数:

           ┌───┐
      byte │   │
           └───┘
           ┌───┬───┐
     short │   │   │
           └───┴───┘
           ┌───┬───┬───┬───┐
       int │   │   │   │   │
           └───┴───┴───┴───┘
           ┌───┬───┬───┬───┬───┬───┬───┬───┐
      long │   │   │   │   │   │   │   │   │
           └───┴───┴───┴───┴───┴───┴───┴───┘
           ┌───┬───┬───┬───┐
     float │   │   │   │   │
           └───┴───┴───┴───┘
           ┌───┬───┬───┬───┬───┬───┬───┬───┐
    double │   │   │   │   │   │   │   │   │
           └───┴───┴───┴───┴───┴───┴───┴───┘
           ┌───┬───┐
      char │   │   │
           └───┴───┘
    

    byte恰好就是一个字节,而longdouble需要8个字节。

    整型

    对于整型类型,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 } }

    特别注意:同一个数的不同进制的表示是完全相同的,例如15=0xf0b1111

    浮点型

    浮点类型的数就是小数,因为小数用科学计数法表示的时候,小数点是可以“浮动”的,如1234.5可以表示成12.345x102,也可以表示成1.2345x103,所以称为浮点数。

    下面是定义浮点数的例子:

    float f1 = 3.14f;
    float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
    double d = 1.79e308;
    double d2 = -1.79e308;
    double d3 = 4.9e-324; // 科学计数法表示的4.9x10^-324

    对于float类型,需要加上f后缀。

    浮点数可表示的范围非常大,float类型可最大表示3.4x1038,而double类型可最大表示1.79x10308。

    布尔类型

    布尔类型boolean只有truefalse两个值,布尔类型总是关系运算的计算结果:

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

    Java语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1 bit,但是通常JVM内部会把boolean表示为4字节整数

    字符类型

    字符类型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);
        }
    }
    A
    中

    注意char类型使用单引号',且仅有一个字符,要和双引号"的字符串类型区分开。

    常量

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

    inal double PI = 3.14; // PI是一个常量
    double r = 5.0;
    double area = PI * r * r;
    PI = 300; // compile error!

    常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。

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

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

    定义变量时,要遵循作用域最小化原则,尽量将变量定义在尽可能小的作用域,并且,不要重复使用变量名。

    小结

    Java提供了两种变量类型:基本类型和引用类型

    基本类型包括整型,浮点型,布尔型,字符型。

    变量可重新赋值,等号是赋值语句,不是数学意义的等号。

    常量在初始化后不可重新赋值,使用常量便于理解程序意图

    整数运算

     Java的整数运算遵循四则运算规则,可以使用任意嵌套的小括号。四则运算规则和初等数学一致。例如:

    public class Main {
        public static void main(String[] args) {
            int i = (100 + 200) * (99 - 88); // 3300
            int n = 7 * (5 + (i - 9)); // 23072
            System.out.println(i);
            System.out.println(n);
        }
    }

    整数的数值表示不但是精确的,而且整数运算永远是精确的,即使是除法也是精确的,因为两个整数相除只能得到结果的整数部分:

    int x = 12345 / 67; // 184

    求余运算使用%

    int y = 12345 % 67; // 12345÷67的余数是17

    特别注意:整数的除法对于除数为0时运行时将报错,但编译不会报错。

    溢出

    要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果

    public class Main {
        public static void main(String[] args) {
            int x = 2147483640;
            int y = 15;
            int sum = x + y;
            System.out.println(sum); // -2147483641
        }
    }

    要解释上述结果,我们把整数214748364015换成二进制做加法:

      0111 1111 1111 1111 1111 1111 1111 1000
    + 0000 0000 0000 0000 0000 0000 0000 1111
    -----------------------------------------
      1000 0000 0000 0000 0000 0000 0000 0111

    由于最高位计算结果为1,因此,加法结果变成了一个负数。

    要解决上面的问题,可以把int换成long类型,由于long可表示的整型范围更大,所以结果就不会溢出:

    long x = 2147483640;
    long y = 15;
    long sum = x + y;
    System.out.println(sum); // 2147483655

    还有一种简写的运算符,即+=-=*=/=,它们的使用方法如下:

    n += 100; // 3409, 相当于 n = n + 100;
    n -= 100; // 3309, 相当于 n = n - 100;

    自增/自减

    Java还提供了++运算和--运算,它们可以对一个整数进行加1和减1的操作:

    public class Main {
        public static void main(String[] args) {
            int n = 3300;
            n++; // 3301, 相当于 n = n + 1;
            n--; // 3300, 相当于 n = n - 1;
            int y = 100 + (++n); // 不要这么写
            System.out.println(y);
        }
    }

    注意++写在前面和后面计算结果是不同的,++n表示先加1再引用n,n++表示先引用n再加1。不建议把++运算混入到常规运算中,容易自己把自己搞懵了。

    移位运算

    在计算机中,整数总是以二进制的形式表示。例如,int类型的整数7使用4字节表示的二进制如下

    00000000 0000000 0000000 00000111

    可以对整数进行移位运算。对整数7左移1位将得到整数14,左移两位将得到整数28

    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

    左移29位时,由于最高位变成1,因此结果变成了负数

    类似的,对整数28进行右移,结果如下:

    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

    如果对一个负数进行右移,最高位的1不动,结果仍然是一个负数

    int n = -536870912;
    int a = n >> 1;  // 11110000 00000000 00000000 00000000 = -268435456
    int b = n >> 2;  // 10111000 00000000 00000000 00000000 = -134217728
    int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
    int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1

    还有一种不带符号的右移运算,使用>>>,它的特点是符号位跟着动,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的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

    byteshort类型进行移位时,会首先转换为int再进行位移。

    仔细观察可发现,左移实际上就是不断地×2,右移实际上就是不断地÷2。

     

    位运算

    位运算是按位进行与、或、非和异或的运算。

    与运算的规则是,必须两个数同时为1,结果才为1

    n = 0 & 0; // 0
    n = 0 & 1; // 0
    n = 1 & 0; // 0
    n = 1 & 1; // 1

    或运算的规则是,只要任意一个为1,结果就为1

    n = 0 | 0; // 0
    n = 0 | 1; // 1
    n = 1 | 0; // 1
    n = 1 | 1; // 1

    非运算的规则是,01互换

    n = ~0; // 1
    n = ~1; // 0

    异或运算的规则是,如果两个数不同,结果为1,否则为0

    n = 0 ^ 0; // 0
    n = 0 ^ 1; // 1
    n = 1 ^ 0; // 1
    n = 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
        }
    }

    上述按位与运算实际上可以看作两个整数表示的IP地址10.0.17.7710.0.17.0,通过与运算,可以快速判断一个IP是否在给定的网段内。

    运算优先级

    在Java的计算表达式中,运算优先级从高到低依次是:

    • ()
    • ! ~ ++ --
    • * / %
    • + -
    • << >> >>>
    • &
    • |
    • += -= *= /=

    类型自动提升与强制转型

    在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型例如,shortint计算,结果总是int,原因是short首先自动被转型为int

    // 类型自动提升与强制转型
    public class Main {
        public static void main(String[] args) {
            short s = 1234;
            int i = 123456;
            int x = s + i; // s自动转型为int
            short y = s + i; // 编译错误!
        }
    }

    也可以将结果强制转型,即将大范围的整数转型为小范围的整数。强制转型使用(类型)例如,将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);
        }
    }

    因此,强制转型的结果很可能是错的。

     整数运算的结果永远是精确的;

    运算结果会自动提升;

    可以强制转型,但超出范围的强制转型会得到错误的结果;

    应该选择合适范围的整型(intlong),没有必要为了节省内存而使用byteshort进行整数运算。

     

  • 相关阅读:
    1167E (尺取法)
    Report CodeForces
    Maximum Xor Secondary CodeForces
    Sliding Window POJ
    单调队列 Sliding Window POJ
    尺取法
    目标
    NOIP系列(续)
    NOIP系列
    近期目标
  • 原文地址:https://www.cnblogs.com/JonaLin/p/12121988.html
Copyright © 2020-2023  润新知