转载

Java程序设计简介

这篇教程介绍了Java编程语言的安装和使用,并且包含一些编程实例。

Java简介

历史

Java 编程语言由Sun微电子公司的James Gosling于1991年创建。1995年发布第一个版本(Java 1.0)。2010年 Sun 微电子公司被 Oracle 公司收购,现在 Java 语言由 Oracle公司控制和管理。2006年Sun宣布Java遵循GNU General Public License (GPL), Oracle继续该项目,即OpenJDK。随着时间的推移,新的增强版本的 Java 已经发布,最新的版本是 Java 1.8 即 Java 8。

Java由规范确定,包含编程语言、编译器、核心库和JVM (运行时Java virtual machine)。Java运行时允许软件开发者用其他语言编码,仍然运行在Java虚拟机上。 Java平台通常与Java虚拟机和Java核心库相关联。

Java 虚拟机

Java 虚拟机(JVM)可以理解为是由软件实现的虚拟计算机,可以像物理计算机一样执行程序代码。Java 虚拟机在不同操作系统下有特定的版本,比如:针对 Linux 操作系统的版本与针对 Windows 操作系统的版本是不一样的。

Java程序由 Java 编译器编译成字节码(bytecode),编译后的字节码由 Java 虚拟机解释执行。 Java程序设计简介

JRE 和 JDK

Java 的发布版本有两种,Java Runtime Environment(JRE)以及 Java Development Kit(JDK)。

Java Runtime Environment(JRE)包含运行 Java 程序需要的必要组件:Java 虚拟机以及 Java 类库。

Java Development Kit(JDK)包含用来创建 Java 应用程序的必要工具,比如,Java 编译器、Java 虚拟机以及 Java 类库。

Java 语言的特点

Java 语言的设计目标是:一次编写到处运行。

Java 语言有以下特点:

  • 平 台无关性:Java 使用 Java 虚拟机运行 Java 程序,Java 虚拟机相当于应用程序和操作系统间的抽象层,应用程序不会直接访问操作系统。这使得 Java 应用程序具备高度的可移植性。一个兼容标准和遵循规则的 Java 应用程序可以无需修改的在所有已支持的平台上工作,例如: Windows 和 Linux。
  • 面向对象的编程语言:除了原生数据类型,Java 语言中一切皆对象。
  • 强类型编程语言:Java 语言是强类型编程语言。比如:变量类型需要预先定义,严格的类型转换检查(大多数情况下有程序猿完成)。
  • 解 释性和编译型语言: Java 源代码被编译成字节码(bytecode)这样使得 Java 语言具备平台独立性。这些字节码(bytecode)指令由 Java虚拟机(JVM)解释执行。JVM 使用 Hotspot 编译技术会将性能相关的字节码指令转换为对用操作系统的二进制代码执行。
  • 自动内存管理: Java 。管理新创建对象的内存分配和回收工作应用程序代码不直接访问内存。由称之为垃圾回收(garbage collector)的机制自动删除没有无引用的对象。

Java 语言的语法与 C++ 语言的语法非常接近,Java 语言是大小写敏感的,比如: myValue 变量与 myvalue 变量是两个不同的变量。

Java语言的开发过程

Java 源代码文件是一个纯文本文档,Java 程序员通常在 Integrated Development Evvironment(IDE)中编写 Java 程序。IDE是帮助程序员完成编码工作的工具,它具备自动格式化代码、语法高亮等功能。

Java 程序员(或 IDE)调用 Java 编译工具(javac)编译源代码,Java 编译工具会将源代码编译成字节码(bytecode)指令。这些指令保存在 .class 文件中由 Java 虚拟机(JVM)来运行。

垃圾回收(Garbage collector)

JVM 自动回收没有被引用的内存空间,它会检查所有对象的引用并查找那些对象可以被自动回收。垃圾回收机制使程序员无需手工管理内存,但是程序员还是需要保证程 序中没有不需要的对象引用,否则垃圾回收机制就无法自动释放对象内存。我们通常把不需要的对象引用通常被称为“内存泄漏”。

Classpath

Java 编译器以及 Java 运行时通过类路径(classpath)来查找和装载 .class文件。比如,如果你打算在应用程序中使用第三方 Java 类库那么你需要把类库的路径添加到你的类路径中,否则你的应用程序无法编译或者运行。

安装 Java

检查安装

你的计算机可能已经安装了 Java,你可以在控制台中使用下面命令来测试 Java 是否已安装(如果你使用 Windows 操作系统,可以按下 Win+R,输入 cmd 后回车即可打开控制台):

java -version

如果你的计算机已经安装了 Java,你应该会看到输出已安装的 Java 版本信息。如果命令行返回应用程序没有找到,那么你就需要安装 Java 了。

安装 Java

在 Ubuntu 操作系统中,你可以使用下面的命令安装 Java:

sudo apt-get install openjdk-7-jdk

对于 Microsoft Windows 操作系统,可以到 Oracle 官网 下载对应的安装包,官网也有相应的文档来指导你如何在其他操作系统上安装 Java。

如 果在安装过程中出现了问题,可以使用“how to install JDK on your_os"关键词在谷歌搜索(对于国内用户则使用”如何在your_os安装JDK“关键词在百度搜索)记住把 "your_os" 替换为你的操作系统名称哦,比如:Windows、Ubuntu、Mac OS X 等等。

2.3 验证安装

回到刚才的命令行(不知道那个?参考2.3节)执行下面的命令:

java -version 

你会得到下面的输出内容:

java version "1.7.0_25" OpenJDK Runtime Environment (IcedTea 2.3.10) (7u25-2.3.10-1ubuntu0.13.04.2) OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

2.4 如何查看当前使用的是32位或64位版本的 Java

在64位操作系统上你可以使用32位或64位版本的Java,如果 java -version 命令的输出中包含 64-bit 这样的字符串说明你正在使用的 Java 版本是64位的,否则你正在使用的 Java 版本是32位的。下面的是64位版本的输出:

java version "1.7.0_25" OpenJDK Runtime Environment (IcedTea 2.3.10) (7u25-2.3.10-1ubuntu0.13.04.2) OpenJDK 64-Bit Server VM (build 23.7-b01, mixed mode)

编写源代码

下面的 Java 代码是在 Linux 操作系统命令行上使用文本编辑器(vim、emacs等)编写的。其他操作系统上也类似,这里就不再做介绍了。

首先需要新建一个目录来保存源代码,这里我们使用目录 /home/vogella/javastarter 。如果你使用 Windows 目录可能是 c:/temp/javastarter ,后面我们会使用 "javadir" 来代表这个路径。

打开一个文本编辑器,不如:Linux操作系统下的 gedit、vim、emacs等,Windows下的 Notepad等,然后输入以下代码:

HelloWorld.java:

// a small Java program  public class HelloWorld {   public static void main(String[] args) {     System.out.println("Hello World");   } }

注意:不要使用富文本编辑器,如:Microsoft Word 或者 LibreOffice 来编写源代码。

将源代码保存到 “javadir” 目录下的 HelloWorld.java 文件中。Java 源文件名称始终要与源代码中得类名一致,并且以 .java 作为后缀。这个例子中源文件名为 HelloWorld.java 因为我们定义的类名是 HelloWorld

编译、运行

打开一个Shell(Linux以及Unix-like)或者命令行(Windows),使用 cd javadir 进入 "javadir" 目录,在我们的例子中命令是 cd /home/vogella/javastarter 。使用 ls (Window中是 dir )来验证源文件是否存在。

使用下面命令编译源文件:

javac HelloWorld.java

命令完成后,重新使用 ls (或者 dir )命令查看目录内容,可以看到目录中多出一个 HelloWorld.class 文件,说明你已经成功的将源代码编译成字节码了。

提示:默认情况下 编译器 会将每个类文件放在和源文件下共同的目录中。你可以在编译时使用 -d 参数来指定不同的目录。

现在可以运行你的第一个 Java 应用程序了。确保你还在 "javadir" 目录,然后执行下面命令来运行程序:

java HelloWorld 

程序会在终端输出 "Hello World" 字符串,参考下图

Java程序设计简介

使用类路径(classpath)

你可以通过指定类路径从其他位置运行应用程序。还是打开Shell或者控制台,然后随便进入一个目录,输入以下命令:

java HelloWorld

如果你当前不在编译后类文件所在的目录,那么 Java 虚拟机 会提示错误:"Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld"。

要正确运行程序,输入下面的命令(将mydirectory替换为你的” java dir"):

java -classpath "mydirectory" HelloWorld

这样你又可以看到 "HelloWorld" 字符串输出了。

Java 语言结构

基础:包(Package)、类(Class)和对象(Object)

了解 Java 的包(Package)、类(Class)和对象(Object)这些基础术语是非常重要的,这部分内容将概要的介绍这些术语。

包(Package)

Java 使用包来组织类,通常按照业务逻辑将类分组到不同的包中。比如:应用程序的所有图形界面可能被分组到 com.vogella.webapplication.views  包中。

通常的做法是使用公司域名的倒序作为顶层包,比如:公司的域名是 "4byte.cn" 那么这个公司 Java 应用的顶层包名可能是 cn.4byte

包的另一个重要用途是避免类命名冲突,类命名冲突是指两个开发人员为他们编写的类使用了同样的全限定名。Java 中类的全限定名是 报名+‘.'+类名,比如: cn.4byte.HelloWorld

如果没有包,当两个程序猿同时给他编写的类起名为 Test  时就会产生命名冲突(而且操作系统也无法创建文件)。结合 Java 包机制,我们可以明确的告诉 虚拟机 我们将使用哪个   Test  类,比如:第一个程序员将   Test  类放到   report  包中,另一个 程序员 将他写得   Test  类放到   xmlreader  包中,那么他们就可以通过全限定名来明确区分两个类    以及  

类(class)

定义:类是一个模板,用来定义对象的数据以及行为,可以理解类为对象的蓝图。

在 Java 中使用 class  关键字来定义类,类名的第一个字母必须大写。类体需要在'{..}'中定义。如:

MyClass.java:

package test;  class MyClass {  }

类的数据保存在属性中,类行为由方法实现。Java 源文件需要以 "类名“ + ". java " 的形式保存。

对象(Object)

定义:对象是类的一个实例。 对象是真实的元素具有数据和可执行的操作。每一个对象都是依据类的定义进行创建的。

继承

一个类可以从另一个类派生,我们称之为子类。另一个常用的说法是:一个类扩展另一个类。被派生(或继承或被扩展)的类我们称之为"父类"。

继承允许子类继承父类的方法和行为(这里还没有提到访问限定问题,会在后面介绍),下面的代码演示了如何继承一个类,Java 是单继承体系(与C++不同)一个类只能有一个父类。

MyBaseClass.java:

package com.vogella.javaintro.base;  class MyBaseClass {   @Override   public void hello() {     System.out.println("Hello from MyBaseClass");   } }  class MyExtensionClass extends MyBaseClass { }

Object是所有类的父类

Java 中所有的类都隐式继承 Object 类。Object 类为每一个 Java 对象定义了下面的一些方法:

  • equals(other)  检查当前对象是否等于other对象

  • getClass()  返回对象的类(Class对象)

  • hashCode()  返回对象的唯一标示符

  • toString()  返回当前对象的字符串描述

Java 接口(interface)

接口(interface)

接口是一个 契约 ,用来描述一个实现类可以完成什么任务,接口并没有去实现契约,契约是由实现接口类来实现的。

接口的定义方法跟类很相似,接口中可以定义方法,接口中只能定义抽象方法,不能定义任何具体方法。接口中定义的方法默认都是:public abstract 方法。

接口中可以定义常量,常量默认是:public static final。

实现接口的类需要实现接口中定义的全部方法(如果不想实现部分方法,那么需要定义这个类为抽象类)。如果重写(override)接口中得方法,可以在方法上使用 @override  注解。

下面是定义接口以及实现接口的代码示例:

MyDefinition.java:

package com.vogella.javaintro.base; public interface MyDefinition {  // constant definition  String URL="http://www.vogella.com";  // define several method stubs  void test();  void write(String s); } 

MyClassImplementation.java:

package com.vogella.javaintro.base;  public class MyClassImplementation implements MyDefinition {    @Override   public void test() {     // TODO Auto-generated method stub    }    @Override   public void write(String s) {     // TODO Auto-generated method stub    } }

接口进化

Java 8以前不能给接口创建新方法。Java的8引入了默认方法,类可以重载默认方法。

方法的多重继承

如果类实现两个接口且这些接口提供相同的默认方法,Java解释规则如下:

  • 父类大于父接口 - 如果类继承父类和父接口的方法,类继承父类方法。
  • 子类型大于父类型。
  • 在其他情况下的类需要实现的默认方法。见下面列子:
public interface A {   default void m() {} }  public interface B {   default void m() {} }  public class C implements A, B {   @Override   public void m() {} }

实现时可以调用父类方法:

public class C implements A, B {   @Override   public void m() {A.super.m();} }

函数式接口(Functional interfaces)

所有只有一个方法的接口称之为函数式接口(Functional interfaces)。函数式接口的优势是可以结合lambda表达式(“闭包”或“匿名方法”)一起使用(函数式编程 )

Java 编译器可以自动识别函数式接口,然而最好在函数式接口上使用 @FunctionalInterface  注解来体现你的设计意图。

一些 Java 标准库中的函数式接口:  

  • java.lang.Runnable  
  • java.util.concurrent.Callable
  •   java.io.FileFilter  
  • java.util.Comparator *
  •   java.beans.PropertyChangeListener

JDK 的 java.util.function  包包含了一些常用的函数式接口:  

Predicate<T>:对象的布尔值属性

Consumer<T>: 对象的 action

Function<T , R>:转换T为R的函数

Supplier<T>:提供T的实例,类似工厂函数。

UnaryOperator<T>: 转换T为T的函数

BinaryOperator<T>: 转换(T, T)为T的函数

Java基础术语

重写方法和@Override 注解

如果一个类继承另一个类,它会继承父类的方法。如果它想要改变父类的一些方法,可重写这些方法。可以在子类中用相同的方法签名重写父类方法。

你可以使用 @Override  注解明确告诉后续的维护代码的程序员以及 Java 编译器,重写了父类对应方法。

下面的代码演示了如何重写父类的方法:

MyBaseClass.java:

package com.vogella.javaintro.base;  class MyBaseClass {   @Override   public void hello() {     System.out.println("Hello from MyBaseClass");   } }

MyExtensionClass2.java:

package com.vogella.javaintro.base;  class MyExtensionClass2 extends MyBaseClass {   public void hello() {     System.out.println("Hello from MyExtensionClass2");   } }

提示: 最好始终在重写父类方法时使用   @Override  注解,这样编译机可以帮助开发人员检查是否正确的重写了父类中对应的方法。

Java 的类型系统

原始数据类型和应用

Java 中主要有两大类类型,原始类型(比如:boolean、short、int、double、float、char 以及 byte)和引用类型(比如:Object 和 String)。

原始数据类型

原始数据类型变量用来描述:数字、布尔值(true/false) 或者字符。原始类型变量不是对象,因此不能通过这些变量执行方法调用。

*,-,+,/ 只能在原始类型上使用,不过   +  可以在字符串上使用,代表字符串拼接。

引用类型

引用类型变量代表到一个对象的引用(或者指针)。如果你修改引用类型变量的值,那么这个变量会指向性的对象或者 nullnull 代表空引用或者引用到一个不存在的对象。修改应用对象变量并不会修改它指向的对象。修改指向对象的内容也不会影响指向它的引用。

类型自动装箱(Autobox)和拆箱(Wrapper)

每一个原始类型都有对应的引用类型(或者说对应的类)。这些引用类型可以在一个对象中保存对应的原始类型。比如: java.lang.Integer  和 int。

将原始类型转换到一个引用类型的实例或者相反的过程称之为:装箱和拆箱。Java 会在必要的情况下自动执行这些过程。这允许你在调用参数为对象的方法时传递原始类型,这个过程称之为自动装箱。

变量和方法

变量

Java 程序在运行过程中使用变量来保存过程值。变量可以是原始类型也可以使引用类型。原始类型变量保存对应的值,而引用类型变量保存的是对象的引用(指针)。因 此,如果你比较两个引用类型变量,你实际上是在比较两个引用类型变量是否指向同一个对象。因此,比较对象时需要使用 object1.equals(object2)  。

实例变量

实例变量定义在对象一级,在对象生命周期内都可以访问。实例变量可以赋予任何访问控制并且可以被标注为 final  或者   transient

被标注为 final  的实例变量在被赋值后是不能被改变的(实际就是只能被赋值一次)。通常情况下,final变量有3个地方可以赋值:直接赋值,构造函数中,或是初始化块中。

由于在java的语法中,声明和初始化是联系在一起的,也就是说:如果你不显示的初始化一个变量,系统会自动用一个默认值来对其进行初始化(如 int就是0)。对于final变量,在声明时,如果你没有赋值,系统默认这是一个空白域,在构造函数进行初始化,如果同时也是是静态的 (static),则可以在初始化块赋值。

局部变量(Local variable)

局部变量不能赋予除 final  以外的访问控制修饰, final  修饰的局部变量在赋值后不可以被改变。

局部变量不会分配默认值,因此需要在使用前初始化它们。

方法

方法是具备参数表以及返回值的代码块,需要通过对象来调用方法,下面是一个 tester  方法的定义:

MyMethodExample.java:

package com.vogella.javaintro.base;  public class MyMethodExample {   void tester(String s) {     System.out.println("Hello World");   } }

方法可以定义可变参数(var-args),定义类可变参数的方法可以接受0个或者多个值(语法: type ... name; ,一个方法只能定义一个可变参数,而且必须是方法参数表的最后一个参数定义。

重写父类方法:子类方法需要与父类方法有完全相同个数和类型的参数以及相同类型的返回值。

重载方法:重载方法是指多个方法有相同的方法名,但是有不同个数或类型参数,返回类型不同不能区分重载方法。

主方法(Main method)

具有 public static  签名的方法可以用来启动 Java 应用程序(主入口),这个方法通常是   main  方法

public static void main(String[] args) {  }

构造函数(Constructor)

类包含它的构造函数,构造函数式在类构造时被调用的方法(执行 new SomeClass 时调用)。构造函数的声明方式与方法类似,唯一的要求就是构造函数名必须与类名相同且不许定义返回类型。

类可以有多个重载的构造函数,参考上一节对重载的描述。每一个类需要有至少一个构造函数。下面构造函数代码示例:

MyConstructorExample2.java:

package com.vogella.javaintro.base;  public class MyConstructorExample2 {    String s;    public MyConstructorExample2(String s) {     this.s = s;   } }

如果代码中没有明确编写构造函数,编译器会在编译期间隐式添加一个,如果一个类继承与其他类,那么父类的构造函数会被隐式调用。

下面的例子中不需要定义一个无参的空构造函数,如果类中没有定义构造函数,那么编译器会在编译期间为你定义一个构造函数:

MyConstructorExample.java:

package com.vogella.javaintro.base;  public class MyConstructorExample {    // unnecessary: would be created by the compiler if left out   public MyConstructorExample() {   } }

构造函数的命名约定是: classname (Parameter p1, ...) { } 。每一个对象是基于构造函数创建的,构造函数是对象可以使用之前被调用的第一个方法。

修饰符(Modifiers)

访问控制修饰符

有三个用于访问控制的关键字: publicprotected  和   private

和 4 种访问控制级别: publicprotecteddefault  以及   private ,这些级别用来定义元素对其他组件的可见性。

如果某些元素被声明为 public 。比如:类或者方法,那么它们可以由其它 Java 对象创建或访问。如果某些元素被声明为   private ,比如一个方法,那么这个方法就只能被定义它的类中的元素访问。

访问级别 protected  和   default  很相似,一个   protected  的类只能被同一个包中的类或者它的子类(同一个包或者其他包)访问, default  访问级别的类只能被同一个包中的类访问。

下表是访问级别的总结。

表1. 访问级别

修饰符 子类 全局
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

其他修饰符

  • final  方法:不可以被子类重写

  • abstracct  方法:抽象方法,没有实现的方法

  • synchronized  方法:线程安全的方法,可以是   final  方法以及赋予其他任何访问控制

  • native  方法:这种方法用来编写平台相关代码(比如:针对 Linux、Windows或Mac OS X等特定操作系统的本地代码)

  • strictfp :   strictfp  关键字可应用于类、接口或方法。使用   strictfp  关键字声明一个方法时,该方法中所有的   float   double 表达式都严格遵守FP-strict的限制,符合IEEE-754规范。当对一个类或接口使用   strictfp  关键字时,该类中的所有代码,包括嵌套类型中的初始设定值和代码,都将严格地进行计算。严格约束意味着所有表达式的结果都必须是 IEEE 754 算法对操作数预期的结果,以单精度和双精度格式表示。

import语句

使用 import 语句

在 Java 开发中我们需要使用类的全名来访问类,比如:cn.4byte.some_package.SomeClass。

你可以在类中使用 import 语句引入一些类或包,这样你在类中可以不用使用全名称来访问引入的类。

静态导入

static import 特性可以让我们在类中使用公共静态类( public static 定义的类)中的成员(方法、属性),而不需要在使用时指定定义成员的类。

该功能提供了一种类型安全的机制,让我们在代码中使用常量,而不必引用最初定义的常量的类。

更多的java语言结构

类方法和类变量

与实例方法和实例变量不同,类方法和类变量是关联类的。需要使用 类名+'.'+方法名或变量名的方式访问类方法和类变量,如: SomeClass.someMethod 或者  SomeClass.someVariable

类方法和类变量需要使用 static 关键字来定义,类方法通常称为静态方法,类变量通常称为静态变量或者静态属性。

类变量的一个例子就是使用 System.out.println("Hello World") 调用  println 函数,这里的  out 就是一个静态域(静态变量),它是  PrintStream 的实例,我们通过它来调用  println 方法。

当你定义类变量后,Java 运行时环境在加载类时就会会为类变量分配固定的内存以及访问地址,因此无论类有多少个实例它的类变量始终指向相同的内存地址。我们可以将类变量理解为全局变量。下面的代码演示如何使用 static 域:

MyStaticExample.java

package com.vogella.javaintro.base;  public class MyStaticExample {    static String PLACEHOLDER = "TEST";    static void test() {     System.out.println("Hello");   } }

Main.java:

package com.vogella.javaintro.base;  public class Tester {    public static void main(String[] args) {     System.out.println(MyStaticExample.PLACEHOLDER);     MyStaticExample.test();   }  }

如果想要将变量定义为常量,可以使用 static final 关键字声明这个变量。

静态方法只能通过类来访问,不可以使用类的实例访问,它不能直接访问类中的非静态变量和方法。

抽象类和抽象方法

类以及方法可以被声明为抽象的 abstract 。如果一个类包含至少一个抽象方法(只有方法声明,没有方法实现)那么这个类就是一个抽象类,它也需要使用  abstract 关键字声明,并且这个类是不能够被直接实例化的。抽象类的子类需要实现抽象类中的抽象方法,除非子类也是抽象类。

下面是抽象类定义的代码示例:

MyAbstractClass.java:

package com.vogella.javaintro.base;  public abstract class MyAbstractClass {   abstract double returnDouble(); }

备忘清单

下面列表是你将要做的事情的参考。

12.1 使用类

在 Java 开发中你需要编写很多的类、方法、和实例变量,下面的代码使用 test 作为包名。

表格2

What to do How to do it
创建MyNewClass类
package test;  public class MyNewClass {  }
创建 var1 变量
package test;  public class MyNewClass {   private String var1; }
创建构造函数,为 var1 赋值
package test;  public class MyNewClass {   private String var1;    public MyNewClass(String para1) {     var1 = para1;     // or this.var1= para1;   } }
创建doSomeThing方法
package test;  public class MyNewClass {   private String var1;    public MyNewClass(String para1) {     var1 = para1;     // or this.var1= para1;   }    public void doSomeThing() {    }  }
创建doSomeThing2方法
package test;  public class MyNewClass {   private String var1;    public MyNewClass(String para1) {     var1 = para1;     // or this.var1= para1;   }    public void doSomeThing() {    }    public void doSomeThing2(int a, Person person) {    }  }
创建doSomeThing3方法。
package test;  public class MyNewClass {   private String var1;    public MyNewClass(String para1) {     var1 = para1;     // or this.var1= para1;   }    public void doSomeThing() {    }    public void doSomeThing2(int a, Person person) {    }    public int doSomeThing3(String a, String b, Person person) {     return 5; // any value will do for this example   }  }
创建 MyOtherClass 类,以及两个属性 myvalue 和 dog
package test;  public class MyOtherClass {   String myvalue;   Dog dog;    public String getMyvalue() {     return myvalue;   }    public void setMyvalue(String myvalue) {     this.myvalue = myvalue;   }    public Dog getDog() {     return dog;   }    public void setDog(Dog dog) {     this.dog = dog;   } }

使用局部变量

局部变量只能在方法中定义

表格3

What to do How to do it
创建 String 类型的局部变量 String variable1;
创建 String 类型的局部变量,并赋值 "Test" String variable2 = "Test";
创建 Person 类型的局部变量 Person person;
创建 Person 类型的局部变量 ,并创建 Person 对象赋值给它 Person person = new Person();
创建字符串数组 String array[];
创建 Person 数组并指定数组长度为 5 Person array[]= new Person[5];
创建局部变量 var1 赋值 5 var1 = 5;
将 pers1 指向 pers2 pers1 = pers2;
创建 ArrayList 元素类型为 Person ArrayList<Person> persons;
创建新的 ArrayList 并赋值给 persons persons = new ArrayList<Person>();
创建 ArrayList 元素类型为 Person,并实例化 ArrayList<Person> persons = new ArrayList<Person>();

集成开发环境(IDE)

前面的章节介绍了如何在Shell(命令行)中创建和编译 Java 应用程序。 Java集成开发环境(IDE)提供了大量用于创建Java程序的易用功能。有很多功能丰富的 IDE 比如:Eclipse IDE

更多的信息可以参考 教程 。

术语 “创建一个 Java 项目(Create a Java project)”在这里可以理解为在 Eclipse 中创建一个 Java 项目。

练习:创建 Java 对象和方法

创建 Person 类并且实例化它

  • 创建一个 Java 项目叫 exercises1 并且使用 com.vogella.javastarter.exercises1 作为包名。

  • 创建 Person 类,并且为这个类增加三个实例变量: firstNamelastName 和  age

  • 使用 Person的 构造函数设置默认值。

  • 添加下面的 toString方法并完善TODO内容,用于转换对象为字符串表示。
  • @Override public String toString() {     // TODO replace "" with the following:     // firstName + " " + lastName   return "";  }
  • 创建 Main 类,并定义方法  public static void main(String[] args) ,在这个方法中创建  Person 类的实例。

使用构造函数

为你的 Person 类添加构造函数,构造函数定义两个参数用来传递 first name 和 last name。并将这两个参数的值赋给对应的实例变量。

定义 getter 和 setter 方法

定义用来读取和设置实例变量值的方法,这些方法被称为 getter 和 setter 方法。

Getter 方法命名方式是: get +实例变量名(实例变量名首字符需要大写)比如:   getFirstName()

Setter 方法命名方式是: set +实例变量名(实例变量名手字符需要大写)比如: setFirstName(String value)

修改 main 方法,使用 getter 和 setter 方法修改  Person 对象的 last name字段。

创建 Address 对象

创建 Address 类用来保存  Person 的地址。

Person 对象中增加一个新的  Address 类型实例变量,以及对应的 getter 和 setter 方法。

答案:创建 Java 对象以及方法

创建 Person 类并实例化

Person.java:

package com.vogella.javastarter.exercises1;  class Person {   String firstname = "Jim";   String lastname = "Knopf";   int age = 12;    @Override   public String toString() {     return firstname + " " + lastname;   }  }

Main.java:

package com.vogella.javastarter.exercises1;  public class Main {     public static void main(String[] args){         Person person = new Person();         System.out.println(person);     }  }

使用构造函数

Person.java:

package com.vogella.javastarter.exercises1;  class Person {   String firstname;   String lastname;   int age;      public Person(String a, String b, int value){       firstname = a;       lastname = b;       age = value;   }    @Override   public String toString() {     return firstname + " " + lastname;   }  }

Main.java:

package com.vogella.javastarter.exercises1; public class Main {  public static void main(String[] args){   Person p1 = new Person("Jim", "Knopf" , 12);   System.out.println(p1);   Person p2 = new Person("Henry", "Ford", 104);   System.out.println(p2);    } } 

定义 getter 和 setter 方法

Person.java:

package com.vogella.javastarter.exercises1; class Person {   String firstname;   String lastname;   int age;   public Person(String a, String b, int value){    firstname = a;    lastname = b;    age = value;   }   public String getFirstName(){    return firstname;   }   public void setFirstName(String firstName){    this.firstname = firstName;   }   public String getLastName(){    return lastname;   }   public void setLastName(String lastName){    this.lastname = lastName;   }   public int getAge() {  return age;   }   public void setAge(int age) {  this.age = age;   }   @Override   public String toString() {  return firstname + " " + lastname;   } } 

Main.java:

package com.vogella.javastarter.exercises1; public class Main {  public static void main(String[] args){   Person p2 = new Person("Jill", "Sanders", 20);   p2.setLastName("Knopf");   System.out.println(p2);  } } 

创建 Address 对象

Address.java:

package com.vogella.javastarter.exercises1;  public class Address {    private String street;   private String number;   private String postalCode;   private String city;   private String country;      public String getStreet() {         return street;       }    public void setStreet(String street) {     this.street = street;   }    public String getNumber() {     return number;   }    public void setNumber(String number) {     this.number = number;   }    public String getPostalCode() {     return postalCode;   }    public void setPostalCode(String postalCode) {     this.postalCode = postalCode;   }    public String getCity() {     return city;   }    public void setCity(String city) {     this.city = city;   }    public String getCountry() {     return country;   }    public void setCountry(String country) {     this.country = country;   }    public String toString() {     return street + " " + number + " " + postalCode + " " + city + " "         + country;   }  }

Person.java:

package com.vogella.javastarter.exercises1;  class Person {   String firstname;   String lastname;   int age;   private Address address;      public Person(String a, String b, int value){       firstname = a;       lastname = b;       age = value;   }      public String getFirstName(){       return firstname;   }      public void setFirstName(String firstName){       this.firstname = firstName;   }      public String getLastName(){       return lastname;   }      public void setLastName(String lastName){       this.lastname = lastName;   }       public int getAge() {     return age;   }    public void setAge(int age) {     this.age = age;   }      public Address getAddress() {         return address;   }    public void setAddress(Address address) {     this.address = address;   }    @Override   public String toString() {     return firstname + " " + lastname;   }  }

Main.java:

package com.vogella.javastarter.exercises1;  public class Main {   public static void main(String[] args) {     // I create a person     Person pers = new Person("Jim", "Knopf", 31);     // set the age of the person to 32     pers.setAge(32);      // just for testing I write this to the console     System.out.println(pers);     /*      * actually System.out.println always calls toString, if you do not      * specify it so you could also have written System.out.println(pers);      */     // create an address     Address address = new Address();     // set the values for the address     address.setCity("Heidelberg");     address.setCountry("Germany");     address.setNumber("104");     address.setPostalCode("69214");     address.setStreet("Musterstr.");      // assign the address to the person     pers.setAddress(address);      // dispose reference to address object     address = null;      // person is moving to the next house in the same street     pers.getAddress().setNumber("105");    }  }

基础语句

if-then 和 if-then-else 语句

if-then 语句是控制语句,如果  if 部分表达式的结果为  true 将执行其定义的代码块。当  if 部分表达式结果为  false 则执行  else 代码块(若有的话)。

下面的代码演示了通过两个方法演示了 if-then 以及  if-then-else 语句:

Switch

switch 语句是一个多条件选择执行语句简称开关语句,类似于  if-else 语句。在  switch 的每一个分支里面都必须写  breakbreak 表示退出整个  switch 语句,如果不使用  break 语句则当第一个  case 匹配后,会顺序执行后面的程序代码,而不管后面的  case 是否匹配,直到遇到  break 语句为止,下面是  switch 语句的一个示例:

switch (expression) {   case constant1:     command;     break; // will prevent that the other cases or also executed   case constant2:     command;     break;     ...   default: }  // Example:  switch (cat.getLevel()) {   case 0:     return true;   case 1:     if (cat.getLevel() == 1) {       if (cat.getName().equalsIgnoreCase(req.getCategory())) {         return true;       }     }   case 2:     if (cat.getName().equalsIgnoreCase(req.getSubCategory())) {       return true;     } }

布尔操作

使用 == 来比较两个原始类型是否相同或者比较两个引用类型是否指向同一个对象。使用  equals() 方法比较两个不同的对象是否相等。

&& 和  || 都是短路方法,意思是一旦表达式中某个条件判断结果已经明确时,方法会停止不再执行后面的条件判断。比如: (true || ...) 结果始终是  true(false && ...) 结果始终是  false 。使用示例:

(var !=null && var.method1() ...)

确保在调用 var.method1() 之前变量  var 始终不为  null

表4

Operations Description
== 比较,对于原始类型比较两个值,对于引用比较引用的对象地址
&&、
!= 不等于
a.equals(b) 检查字符串 a 是否等于字符串 b
a.equalsIgnoreCase(b) 检查字符串 a 是否等于字符串 b,忽略大小写
If (value ? false : true) {} 三元操作,如果 value == true 返回 true

循环语句

for 循环

循环处理的语句。Java的for语句形式有两种:一种是和C语言中的for语句形式一样,另一种形式用于在集合和数组之中进行迭代。有时候把这种形式称为增强的 for(enhanced for) 语句,它可以使循环更加紧凑和容易阅读。它的一般形式为  for(;;) 语句; 初始化总是一个赋值语句,它用来给循环控制变量赋初值;条件表达式是一个关系表达式,它决定什么时候退出循环;增量定义循环控制变量每循环一次后按什么方式变化。这三个部分之间用";"分开。例如:

for循环定义:

for(initialization; expression; update_statement) {    //block of code to run  }

示例语句:

public class ForTest {    public static void main(String args[]) {    for(int i = 1; i < 10; i = i+1) {     System.out.println("value of i : " + i);     }   } }

While循环

while 循环语句是一个控制结构,可以重复的特定任务次数。在执行时,如果布尔表达式的结果为真,则循环中的动作将被执行,否则就跳出循环。

语法:

while(expression) {   // block of code to run }

示例:

public class WhileTest {    public static void main(String args[]) {     int x = 1;      while (x < 10) {       System.out.println("value of x : " + x);       x++;     }   } }

do-while 循环

do-while 循环与  while 循环非常类似,区别在于  while 循环先进行条件判断再开始循环, do-while 循环则是先循环在进行条件判断。

语法:

do {   // block of code to run } while(expression);

示例:

public class DoTest {    public static void main(String args[]) {     int x = 1;      do {       System.out.println("value of x : " + x);       x++;     } while (x < 10);   } }

数组

数组是有序数据(同一类型数据)的集合,数组中的项称为元素,数组中的每个元素使用相同的数组名和下标来唯一地确定数组中的元素。数组中的第一个元素下标为 0,第二个元素下标为 1 ... 以此类推。

package com.vogella.javaintro.array; public class TestMain {   public static void main(String[] args) {  // declares an array of integers  int[] array;  // allocates memory for 10 integers  array = new int[10];  // initialize values  array[0] = 10;  // initialize second element  array[1] = 20;  array[2] = 30;  array[3] = 40;  array[4] = 50;  array[5] = 60;  array[6] = 70;  array[7] = 80;  array[8] = 90;  array[9] = 100;   } } 

针对数组和容器增强的 for循环

可以使用下面更简单的 for 循环语句来遍历数组以及容器类,语法为:

for(declaration : expression) {   // body of code to be executed }

用法示例:

package com.vogella.javaintro.array; public class TestMain {   public static void main(String[] args) {  // declares an array of integers  int[] array;  // allocates memory for 10 integers  array = new int[10];  // initialize values  array[0] = 10;  // initialize second element  array[1] = 20;  array[2] = 30;  array[3] = 40;  array[4] = 50;  array[5] = 60;  array[6] = 70;  array[7] = 80;  array[8] = 90;  array[9] = 100;  for (int i : array) {    System.out.println("Element at index " + i + " :"  + array[i]);  }   } } 

19. 字符串

Java 中的字符串

Java 语言中使用 String 类来表示字符串,所有的字符串,如:"hello" 都是这个类的一个实例。字符串是不可变类型,比如:给 String 对象赋新的值会创建一个新的  String 对象。

Java 中的字符串池

Java 使用 String 池来提高字符串对象的内存使用效率。因为 Java 中字符串对象是不可变类型,因此字符串池允许重复使用已存在的字符串而不是每次都创建一个新的。

如果同一个字符串在 Java 代码中被多次使用,Java 虚拟机只会创建一个改字符串实例,并将其保存在字符串池中。

当一个 String 对象创建后,如: String s = "constant" ,字符串 "connstant" 会被保存在池中。不过, new 操作符会强制创建一个新的  String 对象,并为它分配新的内存,比如: String s = new String("constant");

在 Java 中比较字符串

在 Java 中需要使用 equals() 来比较字符串对象,比如: s1.equals(s2) 。使用  == 来比较字符串对象是不正确的,因为  == 是用来比较对象引用是否相同。由于 Java 使用字符串池,因此  == 在某些时候会给出正确的结果。

下面的例子将会得到正确的结果:

String a = "Hello"; String b = "Hello"; if (a==b) {   // if statement is true   // because String pool is used and   // a and b point to the same constant }

下面的比较会返回 false:

String a = "Hello"; String b = new String("Hello"); if (a==b) {  } else {   // if statement is false   // because String pool is used and    // a and b point to the same constant }

警告:当进行字符串比较时,应该总是使用 equals() 方法。

使用字符串

下面的表格列出了常用的字符串方法

表格5:

Command Description
"Testing".equals(text1); 字符串对象 text1 的值等于 "Testing" 时返回 true
"Testing".equalsIgnoreCase(text1); 字符串对象 text1 的值等于 "Testing" 时返回 true。忽略大小写
StringBuffer str1 = new StringBuffer(); 声明并实例化一个 StringBuffer 对象
str.charat(1); 返回字符串中位置 1 的字符
str.substring(1); 删除第一个字符
str.substring(1, 5); 返回第2个至第5个字符
str.indexOf("Test") 在字符串中查找 "Test" 并返回位置
str.lastIndexOf("ing") 从后向前在字符串中查找 "ing" 并返回位置
str.endsWith("ing") 检查字符串是否以 "ing" 结尾
str.startsWith("Test") 检查字符串是否以 "Test" 开头
str.trim() 删除字符串前后的空格
str.replace(str1, str2) 将字符串中的 "str1" 替换为 "str2"
str2.concat(str1); 将 "str1" 拼接到 "str2" 尾部
str.toLowerCase() / str.toUpperCase() 转换字符串为小写或大写
str1 + str2 凭借字符串

String[] array = myString.split("-");

String[] array2 = myString.split(&quot;//.&quot;);

将字符串根据 "-" 分隔成字符串数组                            

Lambda 表达式

什么是 lambda 表达式

Java 编程语言从 Java 8 开始支持 lambda 表达式。Lambda 表达式是可以作为参数使用的一段代码。lambda 表达式允许指定的代码在稍后执行。Lambda 表达式用用于任何函数式接口适用的地方。

lambda 表达式和闭包的区别

lambda 表达式是一个匿名函数,比如:它可以作为参数定义。闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。这意味着闭包可以访问不在他参数列表中的变量,并且可以将闭包赋值给一个变量。

Java 支持 lambda 表达式,但不支持闭包。

引入 lambda 表达式的目的

使用 lambda 表达式比其他 Java 语法结构更加简洁,比如,Java 8 中 Collections 新增了一个  forEach 方法,这个方法可以接受 lambda 表达式,如下例:

List<String> list = new ArrayList<>(); list.add("vogella.com"); list.add("google.com"); list.add("heise.de"); list.forEach(System.out::println);

使用方法引用

在 lambda 表达式中可以使用方法引用,方法引用定义可以通过 CalledFrom::method 来调用的方法,CallFrom 可以是:

  • instance::instanceMethod

  • SomeClass::staticMethod

  • SomeClass::instanceMethod

比如下面代码:

List<String> list = new ArrayList<>(); list.add("vogella.com"); list.add("google.com"); list.add("heise.de"); list.forEach(s-> System.out.println(s));

流(stream)

Java 8 中的流(stream)是什么?

流(stream)是支持串行和并行聚合操作的元素序列。

新增加的Stream API (java.util.stream)引入了在Java里可以工作的函数式编程。这是目前为止对java库最大的一次功能添加,希望程序员通过编写有效、整洁和简明的代码,能够大大提高生产率。

IntStream

用来创建支持串行和并行聚合操作的包含原始 int 类型的元素序列。

package com.vogella.java.streams; import java.util.ArrayList; import java.util.List; import java.util.stream.IntStream; public class IntStreamExample {   public static void main(String[] args) {  // printout the numbers from 1 to 100  IntStream.range(1, 101).forEach(s -> System.out.println(s));  // create a list of integers for 1 to 100  List<Integer> list = new ArrayList<>();  IntStream.range(1, 101).forEach(it -> list.add(it));  System.out.println("Size " + list.size());   } } 

stream 和 lambda 的 Reduction 操作

Reduction 操作接受一个元素序列为输入,反复使用某个合并操作,把序列中的元素合并成一个汇总的结果,参考下面代码:

Task.java:

package com.vogella.java.streams;  public class Task {   private String summary;   private int duration;      public Task(String summary, int duration) {     this.summary = summary;     this.duration = duration;   }   public String getSummary() {     return summary;   }   public void setSummary(String summary) {     this.summary = summary;   }   public int getDuration() {     return duration;   }   public void setDuration(int duration) {     this.duration = duration;   }    }

StreamTester.java:

package com.vogella.java.streams; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; public class StreamTester {   public static void main(String[] args) {  Random random = new Random();  // Generate a list of random task  List<Task> values = new ArrayList<>();  IntStream.range(1, 20).forEach(i -> values.add(new Task("Task" + random.nextInt(10), random.nextInt(10))));  // get a list of the distinct task summary field  List<String> resultList = values.stream().filter(t -> t.getDuration() > 5).map(t -> t.getSummary()).distinct().collect(Collectors.toList());  System.out.println(resultList);  // get a concatenated string of Task with a duration longer than 5 hours  String collect = values.stream().filter(t -> t.getDuration() > 5).map(t -> t.getSummary()).distinct().collect(Collectors.joining("-"));  System.out.println(collect);   } } 

类型转换

如果使用不同类型的变量,Java 需要进行显示的类型转换,下面章节是一些例子。

转换为字符串

参考下面的代码将其他类型对象转换为字符串:

// Convert from int to String String s1 = String.valueOf (10); // "10"  // Convert from double to String String s2 = String.valueOf (Math.PI); // "3.141592653589793" // Convert from boolean to String String s3 = String.valueOf (1 < 2); // "true"  // Convert from date to String String s4 = String.valueOf (new Date()); // "Tue Jun 03 14:40:38 CEST 2003"

22.2 将字符串转换为数字

// Conversion from String to int int i = Integer.parseInt(String); // Conversion from float to int float f = Float.parseFloat(String); // Conversion from double to int double d = Double.parseDouble(String);

从字符串到数字的转换独立于区域设置,比如:它总是使用数字的英语表示方法。在这种表示法中 "8.20" 是一个正确的数字,但德国藏用的 "8,20" 则是一个错误的数字。

要转换类似于德国的数字表示,你需要使用 NumberFormat 类。我们面临的挑战是,当类似于 "98.00" 这类数字表示时,  NumberFormat 会将其转换成  Long 而不是  Double 。如果需要转换成  Double 请参考下面的方法,

private Double convertStringToDouble(String s) {    Locale l = new Locale("de", "DE");   Locale.setDefault(l);   NumberFormat nf = NumberFormat.getInstance();   Double result = 0.0;   try {     if (Class.forName("java.lang.Long").isInstance(nf.parse(s))) {       result = Double.parseDouble(String.valueOf(nf.parse(s)));     } else {       result = (Double) nf.parse(new String(s));     }   } catch (ClassNotFoundException e1) {     e1.printStackTrace();   } catch (ParseException e1) {     e1.printStackTrace();   }   return result; } 

22.3 Double 转换为 int

int i = (int) double;

22.4 SQL 日期类型转换

使用下面的类将 Date 类型转换为 SQL 的 Date类型

package test; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; public class ConvertDateToSQLDate { private void convertDateToSQL(){  SimpleDateFormat template =    new SimpleDateFormat("yyyy-MM-dd");    java.util.Date enddate =    new java.util.Date("10/31/99");    java.sql.Date sqlDate =    java.sql.Date.valueOf(template.format(enddate)); }   public static void main(String[] args) {   ConvertDateToSQLDate date = new ConvertDateToSQLDate();   date.convertDateToSQL();   } } 

计划任务

Java 支持计划任务,计划任务可以被执行一次或多次。

使用 java.util.Timer 和  java.util.TimerTask 来完成计划任务。实现  TimeTask 的对象将会由  Time 在指定的时间间隔执行。

MyTask.java:

package schedule;  import java.util.TimerTask;  public class MyTask extends TimerTask {   private final String string;   private int count = 0;    public MyTask(String string) {     this.string = string;   }    @Override   public void run() {     count++;     System.out.println(string + " called " + count);   }  }

ScheduleTest.java:

package schedule;  import java.util.Timer;  public class ScheduleTest {    public static void main(String[] args) {     Timer timer = new Timer();      // wait 2 seconds (2000 milli-secs) and then start     timer.schedule(new MyTask("Task1"), 2000);      for (int i = 0; i < 100; i++) {       // wait 1 seconds and then again every 5 seconds       timer.schedule(new MyTask("Task " + i), 1000, 5000);     }   } }

提示:开源框架 "quartz" 提供了增强的计划任务功能。参考: http://www.onjava.com/lpt/a/4637 或  http://www.quartz-scheduler.org/

参考资料

英文地址: http://www.vogella.com/tutorials/JavaIntroduction/article.html

http://codex.wiki/post/165971-434

正文到此结束
Loading...