快速Java入门

Java是完全面向对象的语言。Java通过虚拟机的运行机制,实现“跨平台”的理念。我在这里想要呈现一个简单的入门教程,从最简单的“Hello World!”程序见到面向对象的基本概念。希望对你有用。

"Hello World!"

先来看一个HelloWorld.java程序。这个程序在屏幕上打印出一串字符"Hello World!":

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

程序中包括Java的一些基本特征:

  • 类(class):上面程序定义了一个类HelloWorld,该类的名字与.java文件的名字相同。
  • 方法(method):类的内部定义了该类的一个方法main。
  • 语句(statement):真正的“打印”功能由一个语句实现,即: System.out.println("Hello World!");

下面两点有关Java的书写方式:

  • Java中的语句要以;结尾 (与C/C++相同)。
  • 用花括号{}来整合语句,形成程序块。通过程序块,我们可以知道程序的不同部分的范围,比如类从哪里开始,到哪里结束。

编译与运行

Java程序要经过编译器编译才能执行。在Linux或Mac下,可以下载安装Java JDK。我们使用javac来编译。在命令行中输入下面语句编译:

$javac HelloWorld.java

当前路径下,将有一个名为HelloWorld.class的文件生成。

扫描二维码关注公众号,回复: 6232107 查看本文章

使用java命令来运行。Java会搜寻该类中的main方法,并执行。

$java HelloWorld

变量

计算机语言通常需要在内存中存放数据。编程语言可以用变量(variable)的方式来往内存中读写数据。Java也有变量。Java的变量在使用前,要声明变量的类型。变量占据一定的内存空间。不同类型的变量占据不同的大小。Java中的变量类型如下:

存储大小     例值      注释
byte      1byte        3      字节
int       4bytes       3      整数

short     2bytes       3      短整数
long      8bytes       3      长整数
float     4bytes     1.2      单精度浮点数

double    8bytes     1.2      双精度浮点数

char      2bytes     'a'      字符
boolean   1bit      true      布尔值

在Java中,变量需要先声明(declare)才能使用。在声明中,我说明变量的类型,赋予变量以特别名字,以便在后面的程序中调用它。你可以在程序中的任意位置声明变量。 比如:

public class Test
{
    public static void main(String[] args)
    {
        System.out.println("Declare in the middle:");
        int a;
        a = 5;
        System.out.println(a);  // print an integer
    }
}

上面a是变量名。可以在声明变量的同时,给变量赋值,比如 int a = 5。 “变量”的概念实际上来自于C语言那样的面向过程语言。在Java中,所谓的变量实际上是“基本类型” (premitive type)。我将在类的讲解中更多深入。

上面的程序还可以看到,Java中,可用//引领注释。

数组

Java中有数组(array)。数组包含相同类型的多个数据。我用下面方法来声明一个整数数组:

int[] a;

在声明数组时,数组所需的空间并没有真正分配给数组。我可以在声明的同时,用new来创建数组所需空间:

int[] a = new int[100];

这里创建了可以容纳100个整数的数组。相应的内存分配也完成了。

我还可以在声明的同时,给数组赋值。数组的大小也同时确定。

int[] a = new int[] {1, 3, 5, 7, 9};

使用int[i]来调用数组的i下标元素。i用来表示目标元素在数组中的位置,从0开始。其他类型的数组与整数数组相似。

表达式

表达式是变量、常量和运算符的组合,它表示一个数据。1 + 1是常见的表达式。再比如:

public class Test
{
    public static void main(String[] args)
    {
        System.out.println("Declare in the middle:");
        int a;
        a = 5 + 1;
        System.out.println(a);  // print an integer     }
}

上面的5 + 1也是一个表达式,等于6。

1)数学表达式

数学运算,结果为一个数值:

1 + 2                  加法
4 - 3.4                减法
7 * 1.5                乘法
3.5 / 7                除法
7 % 2                  求余数

2)关系表达式

判断表达式是否成立。结果为boolean值,即真假值:

a > 4.2                大于
3.4 >= b               大于等于
1.5 < 9                小于
6 <= 1                 小于等于
2 == 2                 等于
2 != 2                 不等于

3)布林表达式

两个boolean值的与、或、非的逻辑关系。结果是boolean值:

true && false          and
(3 > 1) || (2 == 1)    or
!true                  not

4)位运算

对整数的二进制形式逐位进行逻辑运算,得到一个整数:

&                      and
|                      or
^                      xor
~                      not
5 << 3                 0b101 left shift 3 bits
6 >> 1                 0b110 right shift 1 bit

还有下列运算符:

m++                    变量m加1
n--                    变量n减1
condition ? x1 : x2    condition为一个boolean值。根据condition,取x1或x2的值

控制结构

Java中控制结构(control flow)的语法与C类似。Java的控制结构使用{}来构成隶属于结构的代码块。

1)选择结构(if)

if (conditon1) {
    statements;
    ...
}
else if (condition2) {
    statements;
    ...
}
else {
    statements;
    ...
}

if...else...结构用于表达选择结构。上面的condition是一个表示真假值的表达式,称为条件。statements;是语句。当if后面的条件成立时,将执行隶属于if的代码块,即if后{}内的语句。如果不成立,那么就执行后面的else语句。如果if后面是else if,那么会在if条件失败后,判断else if的条件是否成立。if结构的流程见图:

作为选择结构的练习,你可以写一个Java程序,判断2018年是否是闰年。

2)循环 (while)

while (condition) {
   statements;
}

while结构用于循环。condition也是一个表示真假的条件。当条件为真时,while隶属的代码块就会重复执行。while结构的流程见图:

3)循环 (do... while)

do {
   statements;
} while(condition);  // 注意结尾的; 

do...while也利用循环。它和while的区别在于,会先执行do隶属的代码,再进行条件判断。condition是条件。当条件为真时,do隶属的代码块就会重复执行。do...while流程如图:

4)循环 (for)

for (initial; condition; update) {
   statements;
}

for也是循环结构。它同样根据condition条件,决定是否继续执行for隶属代码块。但在流程上,它在进入循环之前,会先执行一个initial语句。在执行完一次隶属代码块时,会执行一次update语句。流程如图:

5)跳过或跳出循环

在循环中,可以使用:

break; // 跳出循环
continue; // 直接进入下一环

练习:写一个Java程序,计算从1加2,加3…… 一直加到999的总和

6)选择 (switch)

switch(expression) {
   case 1:
       statements;
       break;
   case 2:
       statements;
       break;
   ...
   default:
       statements;
       break;
}

switch也是一种分支的结构。expression是一个表达式。当expression的值和某一case的值相同时,就执行隶属于该case的代码块。

讲到这里,我们做一个总结。我们了解了变量、表达式和控制结构。这些语法在其他高级编程语言中也很常见,如C、C++、Python等。如果你已经有了编程经验,就不会对这些语法感到陌生。众所周知,Java是一门面向对象语言。前一部分的叙述中,我们还没有怎么涉及面向对象的语法。下面,我们将专注于Java面向对象的方面。

面向对象

“对象”是计算机抽象世界的一种方式。“面向对象”可以用很多方式表达。下面是一种并不精确,但比较直观的理解方式:

  1. 世界上的每一个事物都可以称为一个对象(object),比如张三。对象有身份(Identity),状态(State)和行为(Behavior)。
  2. 对象的状态由数据成员(data member)表示。数据成员又被称作域(field)。我们用其他对象作为该对象的数据成员。比如一个表示身高的整数,比如一个鼻子。
  3. 对象的行为由成员方法(member method)表示。我们简称为方法(method)。一个对象可以有多个方法,比如呼吸,睡觉。
  4. 对象可以归类(class),或者说归为同一类型(type)。同一类型的对象有相同的方法,有同类型的数据成员。某个类型的一个对象被称为该类型的一个实例(instance)。

人类与个体

定义类的语法:

class ClassName
{
   member1;
   member2;
   ...
}

我们定义一个human类:

class Human 
{
    void breath()
    {
        System.out.println("hu...hu...");
    }

    int height;
}

在{}范围内,Human类有两个成员: 一个数据成员height,一个方法breath()。

  • 数据成员height是整数类型,可以用于存储一个整数。
  • 方法代表了对象所能进行的动作,也就是计算机所能进行的操作。方法可以接受参数,并能返回值。在breath()的定义中,breath后面的()中为参数列表。由于参数列表为空,所以breath()不接受参数。在breath()之前的void为返回值的类型,说明breath不返回值。

现在,我们创建对象aPerson,并调用对象的方法breath:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human();
        aPerson.breath();
        System.out.println(aPerson.height);
    }

}

class Human
{
    void breath()
    {
       System.out.println("hu...hu...");
    }

    int height;
}

在main方法中,使用new关键字创建对象。即使是来自同一个类的对象,各个对象占据的内存也不相同,即对象的身份也不同。Human aPerson声明了aPerson对象属于Human类,即说明了对象的类型。对象建立后,我们可以用 对象.数据成员 来引用数据成员,使用 对象.方法() 的方式来调用方法。正如我们在后面打印aPerson.height。

调用同一对象的数据成员

方法可以调用该对象的数据成员。比如下面我们给Human类增加一个getHeight()的方法。该方法返回height数据成员的值:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human();
        System.out.println(aPerson.getHeight());
    }

}

class Human
{   
    /**
     * accessor
     */
    int getHeight()
    {
        return this.height;
    }

    int height;
}

我们新增了getHeight方法。这个方法有一个int类型的返回值。Java中使用return来返回值。

注意this,它用来指代对象自身。当我们创建一个aPerson实例时,this就代表了aPerson这个对象。this.height指aPerson的height。this是隐性参数(implicit argument)。方法调用的时候,尽管方法的参数列表并没有this,Java都会“默默”的将this参数传递给方法。

this并不是必需的,上述方法可以写成:

/**
     * accessor
     */
    int getHeight()
    {
        return height;
    }

Java会自己去判断height是类中的数据成员。但使用this会更加清晰。

我们还看到了/** comments */的添加注释的方法。

方法的参数列表

Java中的方法定义与C语言中的函数类似。Java的方法也可以接收参数列表(argument list),放在方法名后面的括号中。下面我们定义一个growHeight()的方法,该方法的功能是让人的height增高:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human();
        System.out.println(aPerson.getHeight());
        aPerson.growHeight(10);
        System.out.println(aPerson.getHeight());
    }

}

class Human
{
    /**
     * accessor
     */ 
    int getHeight()
    {
        return this.height;
    }

    /**
      * pass argument
      */
    void growHeight(int h)
    {
        this.height = this.height + h;
    }

    int height;
}

在growHeight()中,h为传递的参数。在类定义中,说明了参数的类型(int)。在具体的方法内部,我们可以使用该参数。该参数只在该方法范围,即growHeight()内有效。

在调用的时候,我们将10传递给growHeight()。aPerson的高度增加了10。

调用同一对象的其他方法

在方法内部,可以调用同一对象的其他方法。在调用的时候,使用this.method()的形式。我们还记得,this指代的是该对象。所以this.method()指代了该对象自身的method()方法。

比如下面的repeatBreath()函数:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human();
        aPerson.repeatBreath(10);
    }

}

class Human
{
    void breath()
    {
        System.out.println("hu...hu...");
    }


   /**
    * call breath()
    */
    void repeatBreath(int rep)
    {
        int i;
        for(i = 0; i < rep; i++) {
            this.breath();
        }
    }

    int height;
}

为了便于循环,在repeatBreath()方法中,我们声明了一个int类型的对象i。i的作用域限定在repeatBreath()方法范围内部。

数据成员初始化

在Java中,数据成员有多种初始化(initialize)的方式。比如上面的getHeight()的例子中,尽管我们从来没有提供height的值,但Java为我们挑选了一个默认初始值0。

基本类型的数据成员的默认初始值:

  • 数值型: 0
  • 布尔值: false
  • 其他类型: null

我们可以在声明数据成员同时,提供数据成员的初始值。这叫做显式初始化(explicit initialization)。显示初始化的数值要硬性的写在程序中:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human();
        System.out.println(aPerson.getHeight());
    }

}

class Human
{
    /**
     * accessor
     */
    int getHeight()
    {
        return this.height;
    }

    int height = 175;
}

这里,数据成员height的初始值为175,而不是默认的0了。

显式初始化要求我们在写程序时就确定初始值,这有时很不方便。我们可以使用构造器(constructor)来初始化对象。构造器可以初始化数据成员,还可以规定特定的操作。这些操作会在创建对象时自动执行。

定义构造器

构造器是一个方法。像普通方法一样,我们在类中定义构造器。构造器有如下基本特征:

  1. 构造器的名字和类的名字相同
  2. 构造器没有返回值

我们定义Human类的构造器:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human(160);
        System.out.println(aPerson.getHeight());
    }
}

class Human
{
    /**      
     * constructor      
     */
    Human(int h)
    {
        this.height = h;
        System.out.println("I'm born");
    }

    /**
     * accessor
     */ 
    int getHeight()
    {
        return this.height;
    }

    int height;
}

上面的程序会打印

I'm born 160

构造器可以像普通方法一样接收参数列表。这里,构造器Human()接收一个整数作为参数。在方法的主体中,我们将该整数参数赋予给数据成员height。构造器在对象创建时做了两件事:

  • 为数据成员提供初始值 this.height = h;
  • 执行特定的初始操作 System.out.println("I'm born");

这样,我们就可以在调用构造器时,灵活的设定初始值,不用像显示初始化那样拘束。

构造器是如何被调用的呢?我们在创建类的时候,采用的都是new Human()的方式。实际上,我们就是在调用Human类的构造器。当我们没有定义该方法时,Java会提供一个空白的构造器,以便使用new的时候调用。但当我们定义了构造器时,在创建对象时,Java会调用定义了的构造器。在调用时,我们提供了一个参数160。从最后的运行结果中也可以看到,对象的height确实被初始化为160。

初始化方法的优先级

可以看到,如果我们提供显式初始值,那么数据成员就会采用显式初始值,而不是默认初始值。但如果我们既提供显式初始值,又在构造器初始化同一数据成员,最终的初始值将由构造器决定。比如下面的例子:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human(160);
        System.out.println(aPerson.getHeight());
    }

}

class Human
{
    /**      
     * constructor       
     */
    Human(int h)
    {
        this.height = h; 
    }

    /**
     * accessor
     */ 
    int getHeight()
    {
        return this.height;
    }

    int height=170; // explicit initialization
}

运行结果为:

160

对象最终的初始化值与构建方法中的值一致。因此:

构建方法 > 显式初始值 > 默认初始值

方法重载

一个类中可以定义不止一个构造器,比如:

public class Test
{
    public static void main(String[] args)
    {
        Human neZha   = new Human(150, "shit");
        System.out.println(neZha.getHeight()); 
    }

}

class Human
{
    /**
     * constructor 1
     */
    Human(int h)
    {
        this.height = h;
        System.out.println("I'm born");
    }

    /**
     * constructor 2
     */
    Human(int h, String s)
    {
        this.height = h;
        System.out.println("Ne Zha: I'm born, " + s);
    }

    /**
     * accessor
     */ 
    int getHeight()
    {
        return this.height;
    }

    int height;
}

运行结果:

Ne Zha: I'm born, shit 150

上面定义了两个构造器,名字都是Human。两个构造器有不同的参数列表。

在使用new创建对象时,Java会根据提供的参数来决定构建哪一个构造器。比如在构建neZha时,我们提供了两个参数: 整数150和字符串"shit",这对应第二个构建方法的参数列表,所以Java会调用第二个构建方法。

在Java中,Java会同时根据方法名和参数列表来决定所要调用的方法,这叫做方法重载(method overloading)。构建方法可以进行重载,普通方法也可以重载,比如下面的breath()方法:

public class Test
{
    public static void main(String[] args)
    {
        Human aPerson = new Human();
        aPerson.breath(10);
    }

}

class Human
{
    /**
      * breath() 1
      */
    void breath()
    {
        System.out.println("hu...hu...");
    }


   /**
    * breath() 2
    */
    void breath(int rep)
    {
        int i;
        for(i = 0; i < rep; i++) {
            System.out.println("lu...lu...");
        }
    }

    int height;
}


运行结果:

lu...lu... 
lu...lu... 
lu...lu... 
lu...lu... 
lu...lu... 
lu...lu... 
lu...lu... 
lu...lu... 
lu...lu... 
lu...lu...

可以看到,由于在调用的时候提供了一个参数: 整数10,所以调用的是参数列表与之相符的第二个breath()方法。

Java面向对象的基础知识我就聊到这儿。现在做一个总结。我们了解了类和对象的区别和关系,以及对象的数据成员和成员方法。我们用类来创建对象,并通过构造器来初始化对象。这些知识,是进一步了解面向对象编程所必备的。

在这篇入门介绍中,我用一些简单的例子展示了Java的基础语法。这篇文章能让你浅尝Java编程的滋味。如果你了解其他编程语言,那么文中的很多语法看起来并不陌生。语言之间相互借鉴是常事,更何况Java相对年轻,自然借鉴了C语言这样的前辈。Java最有趣的地方是它的面向对象编程。我将在下一篇“Java核心概念”中详细叙述。

小编是一个有着5年工作经验的JAVA程序员,关于java,自己有做材料的整合,一个完整的java编程学习路线,学习材料和工具,能够进我的群723197800获取,免费送给大家,希望你也能凭着自己的努力,成为下一个优秀的程序员。

猜你喜欢

转载自blog.csdn.net/dlwlrmal/article/details/90201307