本节我们将以Windows操作系统为例,编写并执行第一个Java程序。在这之前,请确保你的操作系统上已经安装了JDK
打开 IDE,会提示我们选择工作目录,工作目录用于存放开发者的配置和开发工件。选择好自己的工作目录后直接点击 Launch按钮即可打开 IDE。打开后会显示一个ecplise的欢迎页面。点击 Create a Hello World application。按照向导步骤即可创建并运行我们的 Hello World 程序。右侧区域为创建一个Hello World程序的整体步骤。下面我们一起动手开始吧。第一步:新建一个 Java 工程点击 New -> Java Project下图中Project name为项目名称。将项目命名为 hello(项目名的命名规范:全小写英文),并选择 JRE 为 JavaSE-14,点击 Finish 按钮。这时会出现一个是否创建模块信息的弹窗,点击 Don't Create 按钮。第二步:在工程下新建一个类在刚刚创建好的工程下的src目录上,点击右键 -> New -> Class将类名命名为 HelloWorld,并且选择创建main方法的复选框。点击 Finish 按钮第三步:编写打印语句现在,ecplise 已经为我们在 HelloWorld.java 文件中自动创建了 main 方法,在 main 方法下增加如下打印语句:System.out.println("Hello World");第四步:运行 Java 应用在源代码文件 HelloWorld.java 上点击右键(或直接在源代码中点击右键),选择 Run As -> Java Application我们可以看到控制台已成功打印 Hello World
在 Java 中,程序不是直接被编译为可执行文件,而是被编译为字节码文件, JVM(Java虚拟机)在运行时执行字节码文件。当我们使用 javac 编译器时,Java 源代码文件被编译为字节码文件,字节码文件以扩展名 .class 的形式保存在磁盘上。当程序运行时,字节码文件将被转换为机器代码,并在内存中执行。总的来说,Java 源代码需要被“转换”两次才能被计算机执行:Java 源代码被编译为字节码:由 javac 前端编译器完成;字节码被编译为机器码:由 JVM 的执行引擎完成。下图描述了一个Java程序从编写到编译,再到执行的步骤:
由于本节会涉及到 Java 多线程编程,所以需要你能预先掌握 Java 多线程编程的方法。比如,线程的创建,线程的启动,线程之间的同步和线程之间的通信。在 Java 平台下,创建线程的方法有两种:第一,是创建一个用户自定义的线程类,然后继承 java.leng.Thread 类,同时要覆写它的 run 方法,调用它的 start 方法启动线程。例如:class MyThread extends Thread{ @Override public void run() { super.run(); }}new MyThread().start();第二,是创建一个任务类。首先,实现 Runnable 接口,并且重写它的 run 方法。然后,创建 java.leng.Thread 类的对象,同时将 Runnable 的实例通过 java.lang.Thread 的构造方法传入。最后,调用 java.lang.Thread 的 start 方法启动线程。例如:class MyTask implements Runnable{ @Override public void run() { }}new Thread(new MyTask()).start();
大家可能有个疑问,为什么需要编译程序呢?计算机不能直接执行我们编写的源代码吗?这是由于计算机只能识别由0和1组成的二进制代码。需要通过编译将源代码转换为计算机认识的二进制代码。
介绍 Java 非阻塞式 Socket 编程,就得介绍 Java NIO。Java NIO 是 Java New IO API,有时也解释为 Java Non-blocking IO。通过 Java NIO 可以实现 Java 非阻塞 Socket 编程。Java NIO 是 Java 1.4 支持的,它将 Socket 数据流抽象为一个 Channel(管道),Socket 数据读写是通过 Channel实现的,并且提供了 Buffer 机制,提高数据读写的性能。Java NIO 通常用来编写高性能 Java 服务器程序。在 Java 1.7 以后,Java NIO 对磁盘文件处理得到了增强,可以将 Socket I/O 和 文件 I/O 融合在 Java NIO 中。Java NIO 提供的新的类结构如下:类名称功能说明ServerSocketChannel表示服务端 TCP Socket 的监听 Channel。ServerSocketChannel 提供的工厂方法 open,用于创建它的实例;同时它提供了 accept 方法用于在服务器中接收新的客户端连接请求,返回值是 SocketChannel 类的实例。SocketChannelSocketChannel 表示一个 TCP 通信 Channel,可以通过它的 open 方法创建,也可以通过 ServerSocketChannel 的 accept 方法创建。SelectorJava I/O 事件多路复用机制,用于同时监听多个 Channel 的读、写、监听事件SelectionKey用于表示具体的事件对象ByteBuffer通过 SocketChannel 进行数据读写,依赖 ByteBufferServerSocketChannel 和 SocketChannel 同时支持阻塞式和非阻塞式,默认是阻塞式。可以通过如下的方法,打开非阻塞式。// 配置监听 ServerSocketChannel 为非阻塞模式ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.configureBlocking(false);// 配置服务器新建立的 SocketChannel 为非阻塞模式SocketChannel newSock = serverChannel.accept();newSock.configureBlocking(false);SocketAddress serverAddr = new InetSocketAddress("127.0.0.1", PORT);SocketChannel sock = SocketChannel.open(serverAddr);// 配置客户端 SocketChannel 为非阻塞sock.configureBlocking(false);
38打开IntelliJ IDEA,完成一些初始配置。(建议初次使用的开发者,一直点击下一步采用默认配置即可)。完成初始配置后会出现如下的欢迎窗口。第一步:新建一个 Java 工程点击 Create New Project。选择项目的 SDK。这里会自动识别我们之前所安装的 JDK14。点击 Next 按钮。选中 Create project from template -> Command Line App,表示会新建一个包含 main() 方法的简单 Java 应用。点击 Next 按钮继续。设置项目名称(Project name)为 hello,Project location 为项目的存放目录,Base package是包名,自定义即可。点击 Finish 按钮。第二步:编写输出语句新建项目成功后,IDE 会自动打开项目。点击左侧的 Project 按钮,即可查看项目的目录结构,Main.java 为 IDE 为我们自动创建的模板代码。在 Main.java 的main() 方法中,编写如下输出语句:System.out.println("Hello World!");第三步:运行 Java 应用点击 main() 方法左侧的绿色小箭头,会弹出一些选项,点击 Run 'Main.main()'。IDE 会自动编译并执行 Java 应用,稍等片刻后,在下方的控制台中会输出 Hello World!。想要执行源码,除了上述点击绿色小箭头,也可以在源代码文件中点击鼠标右键,选择Run Main来执行源代码:
实例://封装编码方法public ByteBuf encode(Object obj) { // 1. 创建 ByteBuf 对象 ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer(); // 2. 序列化 Java 对象 byte[] bytes = SerializeUtils.serialize(obj); // 3. 实际编码过程 byteBuf.writeBytes(bytes); return byteBuf;}//序列化工具类public class SerializeUtils{ //序列化方法 public static byte[] serialize(Object obj){ //省略序列化过程 return null; }}代码说明:创建一个 ByteBuf(前面章节详细讲解过);把内容序列化成字节数组;把字节数组写入到 ByteBuf。
本文程序开发环境说明:操作系统: windows 7 以上;开发工具: Eclipse 。此方案可作为学习者的可选择方案之一,有兴趣可了解其它系统或开发工具!
现在,我们有了一个包含 main 方法的 Java 类了,我们可以在main方法中添加一句打印语句。如果要运行这个 Java 程序,我们可以通过点击工具栏中的绿色运行按钮。如果是第一次运行,我们可以选择点击绿色运行按钮旁边的倒三角选项,在弹出的菜单中选择我们 Run As,接着选择我们要运行的类型,如下图所示:此时,Eclipse 会检查项目中哪些文件没有保存,如果有没有保存的文件,将会弹出窗口询问是否保存,如下图所示:选择 OK 后,我们的程序将会运行,如下图所示:另一种常用的运行程序的方式是通过鼠标右键点击 Java 类中的空白处,在弹出的窗口中选择 Run As,如下图所示:
在 Eclipse 中,运行 Java 程序的时候我们可以选择传递参数或作一些设置,这里我们看看如何给我们的 Java 程序传参数。首先,我们添加一些代码,输出我们传入的参数,如下图所示:现在,让我们选择 Run Configurations… 选项,如下图所示:在弹出的运行设置窗口中,我们选择到我们的 HelloWorld 程序,然后选择 Arguments 选项,填写要传的参数,多个参数间使用空格分隔,如下图所示:接着点击 Run,我们将会看到控制台会把我们的参数输出,如下运行结果,如下图所示:
开发者的绝大部分工作都是在这一层完成,通过 Java 编写的应用程序我们可以使用 Android 系统提供的服务来实现我们预期的功能。每一个应用程序都对应一个 Dalvik 虚拟机,利用跨平台性质,基于 Android 框架开发的应用程序可以运行于任何一台安装有 Android 系统的平台。本教程的大多数内容也是针对这一层,我们将带领你建立属于自己的第一个 Android 应用程序,并一步步由浅入深的学习 Android 各个细枝末节,让你可以更好的掌握 Android 应用程层序开发。
本小节我们将学习 Java 多线程,通过本小节的学习,你将了解到什么是线程,如何创建线程,创建线程有哪几种方式,线程的状态、生命周期等内容。掌握多线程的代码编写,并理解线程生命周期等内容是本小节学习的重点。
可以这样来说,在目前开发市场对于程序员的硬性要求中,并发编程占据了重要的位置,不懂并发编程的从业者不是一名合格的软件工程师。尤其是大数据时代的来临,高并发更成为了家常便饭,工作中,你总是绕不开并发编程的任务,比如说,你想写个程序,一边从文件中读取数据,一边还要做实时计算… 所以,想成为一名资深的 Java 后端工程师,并发编程必须要牢牢把握。
编写客户端程序,首先是创建 AsynchronousSocketChannel 实例,通过它的 open 方法完成。然后调用 AsynchronousSocketChannel 的 connect 方法连接服务器,同样是异步调用,不会阻塞调用线程。调用 Future 的 get 方法获取连接结果。剩下客户端数据收发逻辑和服务器的数据收发逻辑一致。客户端完整代码:import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.AsynchronousSocketChannel;import java.util.concurrent.Future;public class AsyncClient { private static final int PORT =56002; public static void main(String[] args) { try (AsynchronousSocketChannel client = AsynchronousSocketChannel.open()) { Future<Void> result = client.connect(new InetSocketAddress("127.0.0.1", PORT)); System.out.println("Async connect the server"); result.get(); String reqMessage = "Hello server!"; ByteBuffer reqBuffer = ByteBuffer.wrap(reqMessage.getBytes()); Future<Integer> writeResult = client.write(reqBuffer); System.out.println("Async send to server:" + reqMessage); writeResult.get(); ByteBuffer inBuffer = ByteBuffer.allocate(128); Future<Integer> readResult = client.read(inBuffer); readResult.get(); System.out.println("Async recv from server:" + new String(inBuffer.array()).trim()); } catch (Exception e) { e.printStackTrace(); } }}
元编程是计算机程序的编写,这些计算机程序将其他程序(或它们本身)作为数据写入或操作,或者在编译时完成部分工作,而这些工作原本可以在运行时完成。在许多情况下,这使程序员可以在与手动编写所有代码相同的时间内完成更多工作,或者为程序提供更大的灵活性,以有效地处理新情况而无需重新编译。或者,更简单地说:元编程是编写在运行时编写代码的代码,以使您的编程更轻松。这听上去是不是很疯狂?简而言之,您可以使用元编程来重新打开和修改类,捕获不存在的方法并即时创建它们,通过避免重复创建DRY(Don’t repeat yourself)代码等等。Ruby常见的开源框架比如Rails、Sinatra都使用了元编程这门技术。
对于一个 Java 程序员而言,能否熟练掌握并发编程是判断他优秀与否的重要标准之一。因为并发编程是 Java 语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的内功。并发编程在开发语言中占据着不可替代的位置。
本小节我们将学习如何使用 Java 语言结合数据库进行编程。注意,学习本小节需要你有一定的 SQL 基础,了解 MySQL 数据库的 基础 CRUD 操作,如果你还不了解 SQL ,推荐先去学习一个非常不错的 wiki 教程,只需掌握前几节的 SQL 初级知识即可。本小节我们将选择开源免费的 MySQL 5.7 作为数据库,可以去官网下载并安装 MySQL,如果你不知如何下载安装,推荐按照这篇文章来做。通过本小节的学习,你将了解到什么是 JDBC,如何连接数据库,如何关闭数据库,JDBC 的新增、查询、更新和删除接口,如何执行批量等内容。
编程范式是计算机编程的基本风格或典范模式。如果说每个编程者都在创造虚拟世界,那么编程范式就是程序员置身其中采用的世界观和方法论。常见的编程范式包括:面向过程编程面向对象编程编程范型提供了程序员对程序执行的看法:在面向过程编程中,程序员认为程序是一系列相互调用的过程或者函数;在面向对象编程中,程序员认为程序是一系列相互作用的对象;而在函数式编程中一个程序会被看作是一个无状态的函数计算的序列。不同的编程语言也会提倡不同的编程范式,一些语言是专门为某个特定的编程范式设计的。例如,C 支持面向过程编程,Java 支持面向对象编程。Python 编程语言支持多种编程范式,应该在不同的应用场景下,选择合适的编程范式。
我们现在对上面的 HelloWorld.java 代码进行解析:public class HelloWorld { ...}这段代码表示我们定义了一个公开类,类名为 HelloWorld,按照约定类名首字母要大写。public 表示这个类是公开的(至于什么是公开的,我们后面再讲),class顾名思义,就是类, public 和 class都是 Java 中的关键字,必须小写。 花括号{} 中间为类的定义。下面我们来看下花括号中间的 main()方法代码: public static void main(String[] args) { ... }这段代码表示我们定义了一个入口方法,注意:入口方法是 Java 程序执行的起点。public 和 static分别表示方法是公开的、静态的,void是方法的返回类型,main后面的括号() 用来将方法的参数括起来,String[]是参数的类型,args是参数的名称。当然,刚刚对于代码的解释略显生涩,作为初学者无需深究每一项的含义。但有一个点请务必牢记:Java 规定,某个类定义的public static void main(String[] args)是Java程序的固定入口方法。在方法内部,有一行代码: System.out.println("Hello World!");这行代码的意义就是将Hello World这一串字符打印在屏幕上。你可以尝试修改这块内容,将代码中的Hello World!,替换为其他内容,例如:427保存源代码,打开命令行,重新编译执行,屏幕上输出的内容将会被成功替换。
我们知道 Java 是平台无关的编程语言,它是运行在 Java 虚拟机之上的,而 Java 虚拟机又是运行在 Native 系统上的。那么,如何通过 Java 程序检测系统本身的字节序呢?可以通过 java.nio.ByteOrder 类来测试当前 Native 系统的字节序。调用 ByteOrder 的 nativeOrder 方法,就能返回系统本身的字节序。另外,ByteOrder 还定义了两个 ByteOrder 类型的常量常用:ByteOrder.BIG_ENDIAN 表示大端序ByteOrder.LITTLE_ENDIAN 表示小端序检测程序也很简单,如下:public static void testByteOrder(){ System.out.println("The native byte order: " + ByteOrder.nativeOrder());}检测结果如下:The native byte order: LITTLE_ENDIAN
前一小节我们介绍了如何创建 Android 项目,本节课程我们学习 Android Studio 如何创建 Java 类代码,如何添加 Resource,如何使用 Android Studio 自带的模板来快速编写应用代码。
本部分会学习如何高效的使用 Android Studio 来编写代码。如何高效的编排界面布局。如何合理的管理项目资源。开发 Android 应用就是编排界面、编写代码的过程。
为了更好地理解编写 TCP 客户端和服务器程序的步骤,下图展示了通过 C 语言 Socket API 编写客户端和服务器程序的过程。图中的矩形方框都是 C 函数,很好的展示了客户端和服务器 Socket 的建立过程。对于 Java 语言来说,只是应用面向对象的思维对上面的过程进行了抽象,下来我们就探讨一下如何编写 Java 客户端和服务器程序。
Spring MVC 提供了很灵活的用户控制器编写方案。通过实现 Spring MVC 提供的 org.springframework.web.servlet.mvc.Controller 接口;public class HelloAction implements org.springframework.web.servlet.mvc.Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { System.out.println("Bean URL "); return null; }}Spring MVC 支持 POJO (普通 JAVA 类)充当控制器。public class HelloAction { public String hello() { return "hello"; }}Tips: 使用 POJO 充当用户控制器,显然要简洁很多。这是常见的编写方案。
1.2.1 准备工作为了确保我们人生中第一个 Java 程序能够顺利执行,请首先确认你已经完成了下面两件事情:已经在你的电脑上成功安装 JDK;有一个文本编辑器。(如 Windows 的记事本,Unix 的 vim)。1.2.2 编写源代码新建一个文本,输入如下内容,将其命名为 HelloWorld.java并保存426Tips:文件名必须与类名同名(类名就是上面第一行代码中class后面的HelloWorld),并且扩展名为 .java,文件名和类名都区分大小写,所以请确保文件名和类名大小写保持一致;类的命名约定以大写字母开头;上述代码中的所有符号,请使用英文半角;别忽略了代码第三行结尾的分号;。下面为实操中的截图。打开记事本,输入源代码:点击文件 -> 保存,将文件保存到磁盘(此处保存在桌面上),注意:这里要将文件名命名为HelloWorld.java,保存类型选择所有文件,编码选择UTF-8:1.2.3 编译后执行现在,打开你常用的命令行工具。(如 Windows 上的命令提示符、MacOS 上的终端),并进入你刚刚保存 Java 源代码的目录。在Windows搜索栏中输入cmd即可打开:使用cd命令,进入到我们刚刚保存源代码的目录(即进入到桌面目录cd Desktop),使用dir命令,查看目录下内容:使用javac命令编译源代码:javac HelloWorld.java执行编译命令后,你会发现同一目录下会生成一个HelloWorld.class文件,这个HelloWorld.class就是我们说的字节码文件。接下来就是通过 JVM 执行字节码文件了,输入下面的命令:java HelloWorld如果一切正常,你会看到屏幕上输出如下内容:祝贺你!成功执行了第一个 Java 程序!Tips:javac 是编译器,而 java 是虚拟机,先使用 javac 编译器编译源代码,再使用java虚拟机执行字节码文件。这就是我们上面提到的 java 源代码被转换两次到执行的过程。java 命令后面的参数是类名。换句话说,我们只需要给虚拟机传递类名作为参数即可,虚拟机会自动查找对应的以.class为扩展名的文件并且执行。
本小节我们将介绍 Java 语言的基础语法,包括我们在编写第一个 Java 程序时已接触过的类、主方法的概念,以及在编程中给源代码文件和类起名字的时候应该注意什么,什么样的名字是可以使用的,什么样的名字是不可以使用的,还有如何为代码添加注释等内容。只有掌握了这些最基础的知识,才能减少后续学习中的困扰,避免踩坑。
上一个小节 C 语言的简介中我们对 C 语言已经有了一个简单的了解。这一节我们就来看一下 C 语言的程序结构,了解了 C 语言的程序结构之后我们才能正式的开始编写 C 语言程序。下面我们来看一个最简单的 C 语言可执行程序:
Java 代码相对比较简单,因为补全的结果是一个字符串数组,补全列表的列表项也都是单个项目,所以这里直接使用ArrayAdapter再好不过(关于 ArrayAdapter 的使用详见 23 节),代码如下:package com.emercy.myapplication;import android.app.Activity;import android.os.Bundle;import android.widget.ArrayAdapter;import android.widget.AutoCompleteTextView;public class MainActivity extends Activity { private AutoCompleteTextView mTextView; private String[] mDataName = {"慕课", "慕课网", "慕课Android教程", "慕斯蛋糕", "慕容复"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.autoCompleteTextView); ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, mDataName); mTextView.setAdapter(adapter); }}首先我们将补全项存入字符串数组中,然后获取 AutoCompleteTextView 对象,创建 ArrayAdapter,最后为 AutoCompleteTextView 对象指定 Adapter 即可。其中在创建 ArrayAdapter 的时候我们传入了一个 id 为android.R.layout.simple_dropdown_item_1line的布局文件,它是 Android 系统为我们内置的专门用于下拉菜单使用的布局文件,其实里面只有一个 TextView 用于显示下拉菜单项,查看源码如下:<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" style="?android:attr/dropDownItemStyle" android:textAppearance="?android:attr/textAppearanceLargePopupMenu" android:singleLine="true" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:ellipsize="marquee" />我们在使用下拉菜单类型的样式时都可考虑直接采用系统样式,最终编译出来屏幕中有一个输入框,我们输入一个“慕”字,会展示以慕开头的所有可补全的字符串,结果如图所示:
在前面的小节中,将函数作为参数,编写程序实现打印正数和负数。下面使用 lambda 表达式重写这个程序:list = [1, -1, 2, -2, 3, -3]def select(list, select_function): for item in list: if select_function(item): print(item)select(list, lambda item: item > 0)select(list, lambda item: item < 0)在第 3 行,定义了函数 select,它与前面的小节定义的函数完全相同在第 4 行,遍历列表 list 在第 5 行,参数 selct_function 是一个函数,用于选择是否选中当前正在遍历的数值在第 8 行,定义了 lambda 表达式lambda 表达式判断输入参数是否为正数将 lambad 表达式作为参数传递给函数 select,函数打印列表中的正数在第 9 行,定义了 lambda 表达式lambda 表达式判断输入参数是否为负数将 lambad 表达式作为参数传递给函数 select,函数打印列表中的负数程序输出结果如下:123-1-2-3