Java的基本使用

1、如何运行一个Java源码

打开文本编辑器,输入以下代码:

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

class用来定义一个类,public表示这个类是公开的,publicclass都是Java的关键字,必须小写,Hello是类的名字,按照习惯,首字母 H 要大写。

这里有一个 main 方法,该方法有一个参数,参数类型是String[],参数名是args。publicstatic用来修饰方法,这里表示它是一个公开的静态方法,void是方法的返回类型。

Java规定,某个类定义的public static void main(String[] args)是Java程序的固定入口方法,因此,Java程序总是从main方法开始执行。

最后,当我们把代码保存为文件时,文件名必须是Hello.java,而且文件名也要注意大小写。注意,文件名跟类名必须完全一致。

如何运行一个Java程序:

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

$ javac Hello.java

$ java Hello
Hello, world!

2、Java的基本知识

Java是面向对象的语言,它的一个程序的基本单位就是class。

Java入口程序规定的方法必须是静态方法,方法名必须为main,括号内的参数必须是String数组。Java的每一行语句必须以分号结束。

一个 .java 文件只能包含一个 public 类,但可以包含多个非 public 类,除了一个 public 类,其它类都只能用 default 修饰。如果有 public类,public 类的类名必须与文件名相同。 

2.1、一次编写,到处执行

Java介于编译型语言和解释型语言之间。编译型语言如C、C++,代码是直接编译成机器码执行,但是不同的平台(x86、ARM等)CPU的指令集不同,因此,需要编译出每一种平台的对应机器码。解释型语言如Python、Ruby没有这个问题,可以由解释器直接加载源码然后运行,代价是运行效率太低。而Java是将代码编译成一种“字节码”,它类似于抽象的CPU指令,然后,针对不同平台编写虚拟机即JVM,不同平台的虚拟机负责加载字节码并执行,这样就实现了“一次编写,到处运行”的效果。

当然,这是针对Java开发者而言。对于虚拟机,需要为每个平台分别开发。为了保证不同平台、不同公司开发的虚拟机都能正确执行Java字节码,SUN公司制定了一系列的Java虚拟机规范。从实践的角度看,JVM的兼容性做得非常好,低版本的Java字节码完全可以正常运行在高版本的JVM上。

2.2、Java的优势

从互联网到企业平台,Java是应用最广泛的编程语言,原因在于:

  1. Java是基于JVM虚拟机的跨平台语言,一次编写,到处运行;

  2. Java程序易于编写,而且有内置垃圾收集,不必考虑内存管理;

2.3、Java的三个版本(Java SE、EE、ME)

随着Java的发展,SUN给Java又分出了三个不同版本:

  • Java SE:Standard Edition(核心、基础)

  • Java EE:Enterprise Edition(开发web应用)

  • Java ME:Micro Edition(无特殊需求不建议学习)

 

简单来说,Java SE就是标准版,包含标准的JVM和标准库,而Java EE是企业版,它只是在Java SE的基础上加上了大量的API和库,以便方便开发Web应用、数据库、消息服务等,Java EE的应用使用的虚拟机和Java SE完全相同。

Java ME就和Java SE不同,它是一个针对嵌入式设备的“瘦身版”,Java SE的标准库无法在Java ME上使用,Java ME的虚拟机也是“瘦身版”。

毫无疑问,Java SE是整个Java平台的核心,而Java EE是进一步学习Web应用所必须的。我们熟悉的Spring等框架都是Java EE开源生态系统的一部分。不幸的是,Java ME从来没有真正流行起来,反而是Android开发成为了移动平台的标准之一,因此,没有特殊需求,不建议学习Java ME。

2.4、JDK、JRE 和 JVM

JDK > JRE > JVM

  • JDK:Java Development Kit
  • JRE:Java Runtime Environment

简单地说,JRE就是运行Java字节码的虚拟机。但是,如果只有Java源码,要编译成Java字节码,就需要JDK,因为JDK除了包含JRE,还提供了编译器、调试器等开发工具。

二者关系如下:

而 JVM 又是什么呢?我们常说的虚拟机也可以指的是 JVM,在JDK下面的的 jre 目录里面有两个文件夹 bin 和 lib,在这里可以认为 bin 里的就是 jvm,lib 中则是 jvm 工作所需要的类库,而 jvm 和 lib 加起来就称为 jre,即 JVM+Lib =JRE。JVM不能单独搞定class的执行,解释class的时候JVM需要调用解释所需要的类库lib。 

JDK的安装及配置可参考:https://www.cnblogs.com/wenxuehai/p/9492355.html

3、数据类型

3.1、基本数据类型

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

1)整数类型:byte,short,int,long

2)浮点数类型:float,double

3)字符类型:char

4)布尔类型:boolean

在定义 long、float 类型的值时,需要在值加上 l、f 后缀。

3.2、基本数据类型占用的字节数和表示的范围

3.2.1、整数类型

  • byte 占 1 个字节,能表示的范围 -128 ~ 127  (-2^7 ~ 2^7-1)
  • short 占 2 个字节,能表示的范围 -32768 ~ 32767  (-2^15 ~ 2^15-1)
  • int 占 4 个字节,能表示的范围 -2147483648 ~ 2147483647  (-2^31 ~ 2^31-1)
  • long 占 8 个字节,能表示的范围 -9223372036854774808 ~ 9223372036854774807  (-2^63 ~ 2^63-1)

3.2.2、浮点数类型

  • float 占 4 个字节,能表示的范围 -2^128  ~  +2^127  (-3.40E+38 ~ +3.40E+38,E表示10的几次方)
  • double 占 8 个字节,能表示的范围 -2^1024  ~  +2^1023  (-1.79E+308 ~ +1.79E+308)

Java 有两种浮点数据类型,第一种 float 对应单精度浮点数,运行速度相比 double 更快,占内存更小,但是当数值非常大或者非常小的时候会变得不精确,精度要求不高的时候可以使用float类型。double 为 64位 表示,将浮点数赋给某个变量时,如果不字面值后面加 f 或者 F,则默认为 double 类型。

float 的精度为7~8位有效数字double 的精度为16~17位有效数字。

 

3.2.3、布尔类型

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

3.3、基本运算和自动类型转换、强制类型转换

在Java中,位移运算属于基本运算,符号是 << 和 >>,即向左位移和向右位移,在Java中只有整数才能位移。

在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型,因为较小类型的值会被自动转型为较大类型的值。

也可以对值进行强制转型,即将大范围的值转型为小范围的值,强制转型使用: (类型)n。

int i = 10;
long l = i;   //自动类型转换

long l =  200l;
int i = (int)l   //强制类型转换

要注意,超出范围的强制转型会得到错误的结果,因为在转型时只会保留较小类型值的字节数,即前面的那些多余的位数就会被扔掉。 

4、Java命名规范

Java 中的命名规范:

  1. 项目名全部小写(xxxyyy)
  2. 包名全部小写(xxxyyy)
  3. 类名和接口名首字母大写,如果由多个单词组成,每个单词的首字母都要大写(XxxYyy)
  4. 变量名、方法名使用首字母小写的驼峰命名法(xxxYyy)
  5. 常量名全部大写,可用下划线分隔开(XXX_YYY)

所有命名规则必须遵循以下规则:

1)、名称只能由字母、数字、下划线、$符号组成

2)、不能以数字开头

3)、名称不能直接使用JAVA中的关键字,但可以包含关键字。

4)、见名知意,坚决不允许出现中文及拼音命名。

5、Java中的方法参数

5.1、可变参数

可变参数用类型...定义,可变参数相当于数组类型:

class Group {
    private String[] names;

    public void setNames(String... namesArr) {
        this.names = namesArr; //这里的namesArr是一个数组来的 
    }
    public String[] getNames() {
        return names;
    }
}
public static void main(String[] args) {
    Group g = new Group();

    g.setNames("Xiao Ming"); 
    System.out.println(Arrays.toString(g.getNames()));  //[Xiao Ming]

    g.setNames();
    System.out.println(Arrays.toString(g.getNames()));  //[]
    
    g.setNames(new String[] {"aaa", "bbb"});  //也可以直接传入一个数组
    System.out.println(Arrays.toString(g.getNames()));  //[aaa, bbb]
}

5.2、参数传递

在 Java 中,一般情况下,在数据做为参数传递的时候,基本数据类型是值传递,引用数据类型是引用传递(地址传递)。(也有一种说法是Java中的方法参数传递方式只有一种:值传递。其实也就是说引用传递时传递的是地址,也相当于是值传递)

基本类型参数的传递,是值的复制,相当于创建了一个值的副本,函数外和函数内的值互不影响。

引用数据类型是引用传递,即传递了地址过去,相当于创建了一个地址的副本,在函数中如果修改了引用数据类型的值,则实际上修改的是该地址指向的值,所以函数外面的值也会改变,因为它们都是同一个地址。

但是 String 类型的值比较特殊,String 类型的值在创建之后不能被改变,所以如果在函数内修改 string 类型的值,相当于重新创建一个对象,并且将传递过来的地址修改为指向新创建的 string 对象(实际上修改的是地址而不是字符串的值)。而传递过来的地址原本就是一个副本,所以函数外面的string类型的值不会发生改变。相对于其他引用类型来说,string类型是修改传递过来的地址,将地址参数改为指向其他地址,所以实际上修改的是地址。而其他引用类型的值是直接修改该地址中的引用类型的值,实际上是直接修改地址参数指向的值,所以函数外面的引用类型值也会随之发生改变。

可以参考:https://www.cnblogs.com/jiangxin007/p/9076696.html

6、声明和初始化

 在 Java 中,局部变量必须经过显式的赋值才能使用它,如果直接使用一个没有被初始化的局部变量,编译器会报错。

但是,如果该变量是类中的成员变量的话,则虚拟机会默认给它一个初始值,无需显式赋值即可引用。

7、Java中的修饰符

Java语言提供了很多修饰符,主要分为以下两类:

  • 访问修饰符
  • 非访问修饰符

修饰符用来定义类、方法或者变量,通常放在语句的最前端。

7.1、访问控制修饰符(public、protected、默认default、private)

Java 中可以使用访问控制符来保护对类、变量、方法和构造方法的访问。

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

  • protected: 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • default (缺省即什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。注意不是写”default“关键字,而是什么都没写。

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

对于类的修饰符只可以用 public 和 default(缺省)。一个 Java 文件除了一个 public 类,其它的类只能缺省修饰。

请注意:下图中的子类指的是不同包中的子类:

子类和父类在同一个包下,只要父类中的变量不是 private 修饰的都可以访问。

子类和父类不在同一个包中,子类只能访问父类中 public 和 protected 修饰的变量。

  

7.2、非访问修饰符(static、final、abstract、synchronize和volatile)

为了实现一些其他的功能,Java 也提供了许多非访问修饰符。

static 修饰符,用来修饰类方法和类变量。

final 修饰符,用来修饰类、方法和变量。final 修饰的类不能够被继承,修饰的方法不能被继承类覆写,修饰的变量为常量,一旦赋值后就不可修改。final修饰的变量可以在声明时即赋值,也可以先声明,后在构造函数或者代码块中赋值。

abstract 修饰符,用来创建抽象类和抽象方法。

synchronizedvolatile 修饰符,主要用于线程的编程。

7.2.1、static 修饰符

类中用 static 修饰的字段和方法被称为静态字段和静态方法。

静态字段和静态方法并不属于实例,而是类本身的字段或者方法。静态字段和静态方法对于所有的实例而言都是只有一个共享“空间”,所有实例都会共享该字段或者方法。

静态字段(类变量):static 关键字用来声明独立于实例对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 对于静态字段,无论修改哪个实例的静态字段,所有实例的该字段都将会被修改了。注意:局部变量不能被声明为 static 变量。

静态方法(类方法):static 关键字用来声明独立于实例对象的静态方法。静态方法内部不能使用类的非静态变量,因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问 this 变量,也无法访问实例字段和非静态方法,它只能访问静态字段和静态方法

对类的静态变量和方法的访问 Java 中是推荐直接使用 类名.变量名 和 类名.方法名 (注意是直接写类名而不是实例名)的方式访问。虽然通过 实例变量.静态字段 实例变量.方法名 也可以访问到静态变量,但这只是因为编译器可以根据实例类型自动转换为类名xxx来访问静态字段或者方法而已。

public static void main(String[] args) {
    System.out.println(Person.number);
}

class Person {
    public static int number;
}

在静态方法中直接调用非静态方法会报错,只能直接调用静态方法,但是可以通过实例化类对象来调用非静态方法:

public class Hello {
    public static void main(String[] args) {
        show();
        show2();                //直接调用非静态方法会编译错误,Cannot make a static reference to the non-static method show2() from the type Hello
        Hello h = new Hello();  //可以通过实例化一个类对象来调用,此时不会报错
        h.show2();
  
    }    
    private static void show() {
        System.out.println("hi");
    }    
    private void show2() {
        System.out.println("hi2");
    }
}    

为什么 static 方法只能调用静态方法,可以参考:https://blog.csdn.net/x6696/article/details/80798471

7.3、default 关键字

在 Java 中,default 关键字的用法不多,只有两种情况会用到:

  • switch语句的时候使用default
  • 在定义接口的时候使用default来修饰具体的方法

请注意:default 关键字和不写修饰符时的默认(default)是不同的概念。

7.4、Java 中方法(变量)修饰符的使用顺序

  1. 访问控制修饰符:public  private  protected  (default)
  2. static 静态;abstract  抽象方法/类
  3. final  常量。可选,不能和abstract共存
  4. 返回值类型
  5. 方法名 / 变量名

8、Java中关于 String 的操作

8.1、创建字符串

在Java中,String是一个引用类型,它本身也是一个class。但是,Java编译器对String有特殊处理,即可以直接用 "xxx" 来表示一个字符串,实际上字符串在String内部是通过一个char[]数组表示的。

String s1 = new String("hello");    //通过new关键字创建字符串
String s1 = "Hello!";               //字面量方式创建字符串,推荐使用
String s2 = new String(new char[] {'H', 'e', 'l', 'l', 'o', '!'}); //Java内部实际上创建字符串

Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的private final char[]字段,以及没有任何修改char[]的方法实现的。

关于字符串的操作有很多方法,但所有关于字符串的修改都不会改变原字符串,而是返回新的字符串,原有的字符串不会发生改变。

public static void main(String[] args) {
    String s = "aA";
    String s2 = s.toLowerCase();
    System.out.println(s);   //aA
    System.out.println(s2);  //aa
}

8.2、字符串的equals()方法

当我们想要比较两个字符串是否相同时,要特别注意,我们实际上是想比较字符串的内容是否相同。必须使用equals()方法而不能用 == ,==判断的是字符串之间的引用即地址是否相同而不是字符串内容。

equals 方法实际上是 Object 类的方法,默认的 equals 方法比较的是两个对象引用是否一样,作用与 == 相同,但 String 类(还有比如File、Date)覆写了该方法,覆写后比较的是内容而不是引用。

public static void main(String[] args) {
    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2);       //true 这里为true实际上只是因为Java编译器在编译期,会自动把所有相同的字符串当作一个对象放入常量池,所以s1和s2的引用是相同的。
    System.out.println(s1.equals(s2));  //true 字符串的内容比较一定要使用equals方法
     
    String s3 = "HELLO".toLowerCase();
    System.out.println(s1 == s3);       //false  使用 == 进行比较会得到错误的结果
    System.out.println(s1.equals(s3));  //true
}

上面代码中,s1 == s2 为 true 是因为在使用字面量方式创建字符串时,如果新创建一个相同内容的字符串对象,Java会将该新建的对象引向常量池中已存在相同内容的对象地址,所以才为 true。当使用new关键字创建时,此时 Java 会重新在堆中创建一个新对象,所以此时用等号比较会是 false。

所以,使用字面量创建字符串更省内存,所以我们推荐使用字面量方式创建字符串。

9、Java中关于数组的操作

9.1、定义一个数组

在 Java 中,使用 ‘类型[] = new 类型[]’ 的形式来定义一个数组。在 Java 中,定义一个数组的同时,必须指定该数组可容纳的元素数量,否则的话编译不会通过。

//定义一个整型数组
int[] ns = new int[5];
//定义一个字符串数组
String[] arr = new String[3];

String[] arr = new String[];  //报错

或者可以在定义数组时直接指定数组的初始元素,这样就不必写出数组大小,编译器会自动推算出数组的大小。

//直接给数组赋值
int[] ns = new int[] { 68, 79, 91, 85, 62 };

//简写(推荐使用)
int[] ns = { 68, 79, 91, 85, 62 };

9.2、Java中数组的特点

Java的数组有几个特点:

  • 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false
  • 数组一旦创建后,大小就不可改变。

数组是引用类型,并且数组大小不可变。如下:

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 指向一个5个元素的数组,后面又指向一个3个元素的数组而已,原有的数组并没有发生改变,只是 ns 的指向发生了改变而已。

9.3、遍历数组

9.3.1、for 循环遍历数组

因为数组的每个元素都可以通过索引来访问,所以我们可以通过for循环就可以遍历数组

int[] ns = { 1, 4, 9, 16, 25 };
for (int i=0; i<ns.length; i++) {
         int n = ns[i];
         System.out.println(n);
}

9.3.2、for each循环遍历数组

for(int n : ns)循环中,直接拿到的是数组的元素,而不是索引。

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

10、Java中的包装类

为了能将基本类型视为对象进行处理,并能连接相关的方法,java为每个基本类型都提供了包装类,在Java 中提供了 8 种 基本数据类型及对应的 8 种包装数据类型

包装类使得一个基本类型的数据变成了类,有了类的特点,可以调用类的方法

10.1、包装类的构造方法

包装类可以将与之对应的基本数据类型作为参数来构造实例,也可以将一个字符串作为参数构造它们的实例

Integer i=new Integer(1);
Integer i=new Integer("123");

Boolean b=new Boolean(true);
Boolean b=new Boolean(“ok");

注意事项: 

1)Boolean类构造方法参数为String类型时,若该字符串内容为 true(不考虑大小写)时,则该 Boolean 对象表示true,否则表示 false。

2)当构造方法参数为 String 类型时,字符串不能为 null,并且该字符串必须可转换为相应的基本数据类型的数据,否则就算编译通过,运行时也会报 NumberFormatException 异常。

10.2、装箱和拆箱

自动装箱即自动将基本数据类型转换成包装类型。

在 Java 5 之前,要将基本数据类型转换成包装类型只能这样做,看下面的代码。

Integer i1 = new Integer(8);
Integer i1 = new Integer("8");

 Java5 之后即支持自动装箱,如下代码:

Integer i3 = 8;    //自动装箱
Integer i2 = Integer.valueOf(8);    //原理就是调用包装类的 valueOf 方法

自动拆箱即自动将包装类型转换成基本数据类型,与自动装箱相反。

int i4 = i3;   //自动拆箱
int i5 = i3.intValue();

上面就是自动拆箱,自动拆箱的原理就是调用包装类的 xxxValue (xxx表示的是基本数据类型的写法)方法

10.3、包装类的应用场景

1)集合类泛型只能是包装类

// 编译报错
List<int> list1 = new ArrayList<>();

// 正常
List<Integer> list2 = new ArrayList<>();

2)成员变量不想有默认值时可以使用包装类

基本数据类型的成员变量都有默认值,如下面代码 status 默认值为 0,如果定义中 0 代表失败,那样就会有问题,这样只能使用包装类 Integer,它的默认值为 null,所以就不会有默认值影响。

private int status;

3) 方法参数允许定义空值

下面的代码,方法参数定义的是基本数据类型 int,所以必须得传一个数字过来,不能传 null,很多场合我们希望是能传递 null 的,所以这种场合用包装类比较合适。

private static void test1(int status){
    System.out.println(status);
}
原文地址:https://www.cnblogs.com/wenxuehai/p/11770117.html