Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/limen/phper-learn-java
帮助PHP开发者快速入门Java
https://github.com/limen/phper-learn-java
java php phper
Last synced: about 1 month ago
JSON representation
帮助PHP开发者快速入门Java
- Host: GitHub
- URL: https://github.com/limen/phper-learn-java
- Owner: limen
- Created: 2020-04-13T06:59:15.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2022-06-21T03:12:13.000Z (over 2 years ago)
- Last Synced: 2023-03-01T16:42:07.474Z (almost 2 years ago)
- Topics: java, php, phper
- Language: Java
- Homepage:
- Size: 66.4 KB
- Stars: 1
- Watchers: 2
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# PHPer learn Java
# 概述
本文面向需要学习Java的PHP开发者。
本文从一个helloworld程序开始,紧接着一个数据库操作demo(这有别于从易到难的风格),然后回归基础,难度逐步加大。把数据库操作demo放在前面,是希望大家快速对一个Java项目形成感性的认识,请不要深究技术细节。
难度最大的部分在于多线程和线程安全,这对很多PHP开发者来说是全新的领域。
最后部分讲解如何用SpingBoot实现RESTful API。
由于作者水平有限,不准确之处在所难免,敬请指正。
# 准备工作
环境依赖- JDK 1.8
- IDE Intelij IDEA
- Maven# Hello, World!
按照惯例先写一个 helloword 程序。
新建文件 HelloWorld.java
```java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
```编译源文件,生成同名的.class文件
```bash
$ javac HelloWorld.java
```执行class文件,无需后缀
```bash
$ java HelloWorld
Hello, World!
```main 方法是Java类的入口方法,方法名称和格式是约定俗成的。
**作业**
- 写一个PHP版的helloword,对比一下两者的区别。
# 难度提升
这一节用Java做点有难度的事:操作数据库。这一节的目标是对Java项目建立感性的认识。相关知识点
- 依赖管理
- Mybatis
- XMLmaven是Java项目依赖管理工具,类似于PHP的composer。
maven使用pom.xml配置依赖,类似于composer.json。
引入两个依赖:mybatis和mysql connector,pom.xml文件内容如下
```xml
4.0.0
com.limengxiang
basics
1.0-SNAPSHOT
aliyun
aliyun
http://maven.aliyun.com/nexus/content/groups/public
aliyun
aliyun
http://maven.aliyun.com/nexus/content/groups/public
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
mysql
mysql-connector-java
8.0.15
runtime
```
项目的目录结构如下
![选区_081.png](https://cdn.nlark.com/yuque/0/2020/png/514450/1586431803443-3b9b8245-f746-4fa6-a17f-38e7872cdd12.png#align=left&display=inline&height=536&name=%E9%80%89%E5%8C%BA_081.png&originHeight=536&originWidth=394&size=26865&status=done&style=none&width=394)
- UserDAO.java:定义CRUD方法
- UserModel.java:定义数据结构
- UserMapper.xml:定义UserDAO方法对应的SQL表结构
```sql
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`mobile` varchar(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
```UserDAO.java
```java
package com.limengxiang.basics.dao;import com.limengxiang.basics.model.UserModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;import java.util.List;
@Mapper
public interface UserDAO {public Integer insert(@Param("username") String username, @Param("mobile") String mobile);
public UserModel selectOne(@Param("tableName") String tableName, @Param("id") Integer id);
public List fuzzySearch(@Param("username") String username, @Param("mobile") String mobile);
public List searchByUsernameOrMobile(@Param("username") String username, @Param("mobile") String mobile);
}
```UserModel.java
```java
package com.limengxiang.basics.model;public class UserModel {
private String username;
private String mobile;public String toString() {
return "username:" + username +
", mobile:" + mobile;
}
}
```UserMapper.xml
```xml
INSERT INTO users (username,mobile) VALUES (#{username}, #{mobile})
SELECT * FROM ${tableName} WHERE id = #{id}
SELECT * FROM users
AND username LIKE #{username}
AND mobile LIKE #{mobile}
SELECT * FROM users
AND username LIKE #{username}
AND mobile LIKE #{mobile}
```
以上三个文件帮助我们定义好了CRUD的业务逻辑。仔细看一下文件内容,试着发现一些规律。
在开始操作数据库之前,还需要配置连接信息,并加载UserMapper.xml文件。
mybatis-config.xml
```xml
```
MybatisDemo.java
```java
package com.limengxiang.basics;import com.limengxiang.basics.dao.UserDAO;
import com.limengxiang.basics.model.UserModel;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.IOException;
import java.io.InputStream;
import java.util.List;public class MybatisDemo {
public static void main(String[] args) {
try {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSession sqlSession = new SqlSessionFactoryBuilder().build(stream).openSession(true);
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
// 插入数据
Integer inserted = userDAO.insert("allen", "13356986723");
System.out.println("Insert result:" + inserted);
inserted = userDAO.insert("alfred", "13856986723");
System.out.println("Insert result:" + inserted);UserModel user = userDAO.selectOne("users", 1);
System.out.println("Select one:" + user);List users = userDAO.fuzzySearch("all%", "");
System.out.println("Fuzzy search by username:" + users);users = userDAO.fuzzySearch("", "133%");
System.out.println("Fuzzy search by mobile:" + users);users = userDAO.fuzzySearch("all%", "133%");
System.out.println("Fuzzy search by username and mobile:" + users);users = userDAO.searchByUsernameOrMobile("all%", "135%");
System.out.println("Search by username:" + users);users = userDAO.searchByUsernameOrMobile("", "135%");
System.out.println("Search by mobile:" + users);} catch (IOException e) {
e.printStackTrace();
}}
}
```运行结果如下,重点关注打印出的SQL,和UserMapper.xml定义的SQL进行对比,试着发现一些规律。
```basic
19:45:14.943 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
19:45:15.094 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
19:45:15.094 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
19:45:15.094 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
19:45:15.094 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
19:45:15.171 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
19:45:15.590 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Created connection 2085002312.
19:45:15.592 [main] DEBUG com.limengxiang.basics.dao.UserDAO.insert - ==> Preparing: INSERT INTO users (username,mobile) VALUES (?, ?)
19:45:15.630 [main] DEBUG com.limengxiang.basics.dao.UserDAO.insert - ==> Parameters: allen(String), 13356986723(String)
19:45:15.638 [main] DEBUG com.limengxiang.basics.dao.UserDAO.insert - <== Updates: 1
Insert result:1
19:45:15.639 [main] DEBUG com.limengxiang.basics.dao.UserDAO.insert - ==> Preparing: INSERT INTO users (username,mobile) VALUES (?, ?)
19:45:15.639 [main] DEBUG com.limengxiang.basics.dao.UserDAO.insert - ==> Parameters: alfred(String), 13856986723(String)
19:45:15.644 [main] DEBUG com.limengxiang.basics.dao.UserDAO.insert - <== Updates: 1
Insert result:1
19:45:15.662 [main] DEBUG com.limengxiang.basics.dao.UserDAO.selectOne - ==> Preparing: SELECT * FROM users WHERE id = ?
19:45:15.662 [main] DEBUG com.limengxiang.basics.dao.UserDAO.selectOne - ==> Parameters: 1(Integer)
19:45:15.691 [main] DEBUG com.limengxiang.basics.dao.UserDAO.selectOne - <== Total: 0
Select one:null
19:45:15.705 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - ==> Preparing: SELECT * FROM users WHERE username LIKE ?
19:45:15.705 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - ==> Parameters: all%(String)
19:45:15.708 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - <== Total: 1
Fuzzy search by username:[username:allen, mobile:13356986723]
19:45:15.709 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - ==> Preparing: SELECT * FROM users WHERE mobile LIKE ?
19:45:15.709 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - ==> Parameters: 133%(String)
19:45:15.710 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - <== Total: 1
Fuzzy search by mobile:[username:allen, mobile:13356986723]
19:45:15.710 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - ==> Preparing: SELECT * FROM users WHERE username LIKE ? AND mobile LIKE ?
19:45:15.711 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - ==> Parameters: all%(String), 133%(String)
19:45:15.712 [main] DEBUG com.limengxiang.basics.dao.UserDAO.fuzzySearch - <== Total: 1
Fuzzy search by username and mobile:[username:allen, mobile:13356986723]
19:45:15.713 [main] DEBUG com.limengxiang.basics.dao.UserDAO.searchByUsernameOrMobile - ==> Preparing: SELECT * FROM users WHERE username LIKE ?
19:45:15.713 [main] DEBUG com.limengxiang.basics.dao.UserDAO.searchByUsernameOrMobile - ==> Parameters: all%(String)
19:45:15.714 [main] DEBUG com.limengxiang.basics.dao.UserDAO.searchByUsernameOrMobile - <== Total: 1
Search by username:[username:allen, mobile:13356986723]
19:45:15.715 [main] DEBUG com.limengxiang.basics.dao.UserDAO.searchByUsernameOrMobile - ==> Preparing: SELECT * FROM users WHERE mobile LIKE ?
19:45:15.715 [main] DEBUG com.limengxiang.basics.dao.UserDAO.searchByUsernameOrMobile - ==> Parameters: 135%(String)
19:45:15.716 [main] DEBUG com.limengxiang.basics.dao.UserDAO.searchByUsernameOrMobile - <== Total: 0
Search by mobile:[]
```# 回归基础
我们继续从基础开始学习,打好基础才能走得更远。
## 静态与动态,强类型与弱类型
Java是静态语言,PHP是动态语言;Java是强类型语言,PHP是弱类型语言。
静态的含义是在编译期就能确定变量的数据类型,动态则不能。这也属于强弱类型的范畴。
Java语言在声明变量时需要指定数据类型,这确保了“静态”,PHP则相反。
## 数据类型
基本数据类型
- short
- int
- long
- float
- double
- char
- byte
- boolean包装器类型
- Short:short
- Integer:int
- Long:long
- Float:float
- Double:double
- Character:char
- Byte:byte
- Boolean:boolean包装器类封装了基本类型常用的一些API,例如将字符串解析为整数。
除基本数据类型外,一切皆对象!
**思考**
- PHP有哪些数据类型?跟Java对比有什么区别?
## 类与继承
PHP和Java都是面向对象的编程语言。
PHP的面向对象借鉴了Java的设计,例如
- 继承关键词:extends,implements
- 不允许多继承,但可以实现多个接口
- 抽象类、抽象方法熟悉PHP面向对象的开发者,学习Java面向对象不会有难度。这里不赘述了。
## 包与命名空间
在Java文件的顶部可以看到 package ***
这声明了Java类所属的包,和PHP的命名空间(namespace)类似。Java对类的命名要求更严格一些,例如类名(public类)必须和文件名保持一致。
# 难度Level 2
## 方法签名
先看一个例子
```java
public class HelloWorld {public void hello(String name) {
System.out.println("Hello, " + name);
}public void hello(int age) {
if (age < 20) {
System.out.println("Hello son");
} else if (age >= 20 && age < 40) {
System.out.println("Hello buddy");
} else if (age >= 40) {
System.out.println("Hello old man");
} else {
System.out.println("Hello, you are " + age + " years old");
}
}/**
* 声明该方法会导致编译时报错
* error: method hello(int) is already defined in class HelloWorld
* 返回值类型不是签名的组成部分
*/
// public int hello(int age) {
// return age;
// }public static void main(String[] args) {
System.out.println("Hello, World!");
HelloWorld hw = new HelloWorld();
hw.hello("Tom");
hw.hello(60);
}
}
输出结果
Hello, World!
Hello, Tom
Hello old man```
可以看到HelloWorld类定义了两个public void hello方法,这在PHP中是不允许的。
这涉及到“方法签名”的概念,在Java类中,相同名称但签名不同的方法是可以共存的。
方法名和方法参数类型列表构成了一个方法的签名,上面例子中两个hello方法的签名分别是
- hello(String)
- hello(int)**作业**
- 编写一个Java类,实现多个构造方法。
## 内部类
看以下栗子。
咦!HelloWorld类里面怎么多了一个Person类!不要惊慌,这就是接下来要说的“内部类”。
```java
public class HelloWorld {class Person {
private String name;
private int age;public Person(String name, int age) {
this.name = name;
this.age = age;
}public String toString() {
return name + ", " + age + " years old";
}
}public void hello(Person person) {
System.out.println("Hello, " + person);
}public void helloPerson(String name, int age) {
Person person = new Person(name, age);
hello(person);
}public static void main(String[] args) {
System.out.println("Hello, World!");
HelloWorld hw = new HelloWorld();
hw.helloPerson("Tom", 25);
}
}
输出结果
Hello, World!
Hello, Tom, 25 years old
```在一个Java类中定义另一个类,就形成了内部类。
- 内部类是**依赖**外部类的对象存在的
- 内部类可以**无条件访问**外部类的属性和方法内部类允许我们更好地封装业务代码,当这些封装仅限于内部使用时。PHP则不支持内部类。
**作业**
- 尝试从外部类访问内部类的属性和方法
- 尝试在main方法中生成Person对象## 注解与反射
注解的英文名称是 annotation,顾名思义,就是给代码加个标记。举个例子,当你收到一封秘密信件,信封上写着“请务必亲手交给xxx”,你就知道了,这个信件是要转交给别人的,自己不能打开。注解的作用大致也是如此。
框架管理一个对象时,会检查对象的注解,例如上文中的UserDAO类,被@Mapper注解修饰,框架就知道这里定义的是CRUD方法,需要去xml配置文件中查找对应的SQL。注解的检查工作是通过“反射”机制实现的,PHP也有反射机制,这里不再赘述。
# 难度Level 3
## 线程与多线程
直接上代码
```java
import java.util.ArrayList;class ThreadDemo {
class MyThread extends Thread {private int id;
MyThread(int id) {
this.id = id;
}/**
* 线程的业务逻辑
*/
@Override
public void run() {
System.out.println("Thread #" + id);
}
}public void run(int num) {
// 声明一个MyThread数组
ArrayList threads = new ArrayList();
for (int i=0; i threads = new ArrayList();
for (int i=0; i
org.springframework.boot
spring-boot-starter-parent
2.2.0.RELEASE
4.0.0com.limengxiang
basics
1.0-SNAPSHOT
... ...
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
... ...
```
创建应用启动文件,MyApplication.java
```java
package com.limengxiang.basics;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;/**
* SpringBootApplication注解告知框架这是启动类
* ComponentScan注解告知框架需要扫描的组件位置
* 这里告知框架扫描controller包下面的所有组件,确保HelloController被扫描
*/@SpringBootApplication
@ComponentScan("com.limengxiang.basics.controller")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class);
}
}
```创建Controller,HelloController.java
```java
package com.limengxiang.basics.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import java.util.Map;/**
* RestController注解告知框架这是一个提供Restful API的组件
*/
@RestController
public class HelloController {/**
* 框架自动获取请求参数传给方法
* name参数必传
* RequestMapping是路由映射注解,可以指定请求路径和请求方法
*
* @param name
* @return
*/
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(@RequestParam("name") String name) {
return "Hello, " + name;
}/**
* 手动从request对象中获取参数
* 框架自动解决依赖,注入request对象
*
* @param request
* @return
*/
@RequestMapping(value = "/params", method = RequestMethod.POST)
public Map params(HttpServletRequest request) {
// 获取一个参数
String name = request.getParameter("name");
System.out.println("param name:" + name);
// 获取所有参数
Map paramMap = request.getParameterMap();
System.out.println("param map:" + paramMap);return paramMap;
}
}
```创建应用配置文件 application.yml,我们使用了mybatis,需要在这里配置连接信息。
这里可以随便写,只要有这几项就OK。
```yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/read_data?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
```打开启动文件,运行MyApplication
```
2020-04-13 15:30:52.815 INFO 13600 --- [ main] com.limengxiang.basics.MyApplication : Starting MyApplication on limengxiang-ubt with PID 13600 (/home/limengxiang/workplace/java/basics/target/classes started by limengxiang in /home/limengxiang/workplace/java/basics)
2020-04-13 15:30:52.820 INFO 13600 --- [ main] com.limengxiang.basics.MyApplication : No active profile set, falling back to default profiles: default
2020-04-13 15:30:55.181 INFO 13600 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-04-13 15:30:55.229 INFO 13600 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-04-13 15:30:55.229 INFO 13600 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.27]
2020-04-13 15:30:55.441 INFO 13600 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-04-13 15:30:55.441 INFO 13600 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2548 ms
2020-04-13 15:30:55.623 INFO 13600 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-13 15:30:55.996 INFO 13600 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-13 15:30:55.998 INFO 13600 --- [ main] com.limengxiang.basics.MyApplication : Started MyApplication in 5.192 seconds (JVM running for 6.485)
```用浏览器访问 [http://127.0.0.1:8080/hello?name=Tom](http://127.0.0.1:8080/hello?name=Tom)
看到“Hello, Tom”就说明成功了。
用curl模拟POST请求,curl -X POST -d 'name=Tom&age=22' 'http://127.0.0.1:8080/params',返回
```
{"name":["Tom"],"age":["22"]}
```**作业**
- 结合mybatis demo,尝试在API中操作数据库
## 单元测试
PHP和Java都需要写单元测试,这是个好习惯。我们以UserDAO为例。打开UserDAO.java,光标定位到类名,按下Alt + Enter键,在弹出的选项中选择“Create test”,勾选需要测试的方法。
![image.png](https://cdn.nlark.com/yuque/0/2020/png/514450/1586764551457-046fded5-1646-4716-b744-cca0e1ed3e4e.png#align=left&display=inline&height=304&name=image.png&originHeight=607&originWidth=994&size=77812&status=done&style=none&width=497)
点“OK”,自动生成测试文件模板,自己填充测试逻辑即可。
```java
package com.limengxiang.basics.dao;import com.limengxiang.basics.model.UserModel;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;import java.io.IOException;
import java.io.InputStream;
import java.util.List;import static org.junit.jupiter.api.Assertions.*;
class UserDAOTest {
private UserDAO dao;
@BeforeEach
void setUp() throws IOException {
if (dao == null) {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSession session = new SqlSessionFactoryBuilder().build(stream).openSession(true);
dao = session.getMapper(UserDAO.class);
}
}@Test
void insert() {
// 插入数据
Integer inserted = dao.insert("allen", "13356986723");
assertEquals(inserted, 1);
}@Test
void selectOne() {
UserModel user = dao.selectOne("users", 1);
System.out.println("Select one:" + user);
}@Test
void fuzzySearch() {
List users = dao.fuzzySearch("all%", "");
System.out.println("Fuzzy search by username:" + users);
}@Test
void searchByUsernameOrMobile() {
List users;
users = dao.searchByUsernameOrMobile("all%", "135%");
System.out.println("Search by username:" + users);users = dao.searchByUsernameOrMobile("", "135%");
System.out.println("Search by mobile:" + users);
}
}
```运行单元测试
![image.png](https://cdn.nlark.com/yuque/0/2020/png/514450/1586764720930-76c4bb0b-3255-4c88-a531-da6bd7f05a31.png#align=left&display=inline&height=138&name=image.png&originHeight=276&originWidth=978&size=45298&status=done&style=none&width=489)
# 总结
恭喜你成为了Java入门级开发者。
由于作者水平有限,只能引领你走到这里。以后的路就靠你自己了。加油。