ORM即Object-Relationl Mapping,它的作用是在關系型數據庫和對象之間作一個映射,這樣,我們在具體的操作數據庫的時候,就不需要再去和復雜的SQL語句打交道,只要像平時操作對象一樣操作它就可以了 。
在ORM出現之前,我們是用jdbc來操作數據庫,但jdbc沒有封裝,對于大項目來說,使用復雜,也難以實現MVC的概念,所以人們就開發了ORM框架來解決這些問題。比如Hibernate,Mybatis,不過Hibernate是完全的ORM框架,mybatis是半ORM框架,因為它需要手動建表和自己寫sql。
ORM的優點: 提高了開發效率。由于ORM可以自動對Entity對象與數據庫中的Table進行字段與屬性的映射,能夠像操作對象一樣從數據庫獲取數據。
ORM的缺點 :ORM的缺點是會犧牲程序的執行效率,因為是自動生成sql,所以實現復雜查詢比較麻煩。
ORM 框架很多,大家各自搞自己的,為了統一下規范,就出現了 JPA。
JPA全稱Java Persistence API,可以通過注解或者XML描述【對象-關系表】之間的映射關系,并將實體對象持
久化到數據庫中。
JPA為我們提供了:
1)ORM映射元數據:JPA支持XML和注解兩種元數據的形式,元數據描述對象和表之間的映射關系,框架據此將實體對象持久化到數據庫表中;
如:@Entity、@Table、@Column、@Transient等注解。
2)API:用來操作實體對象,執行CRUD操作,框架在后臺替我們完成所有的事情,開發者從繁瑣的JDBC和SQL代碼中解脫出來。
如:entityManager.merge(T t);
3)JPQL查詢語言:通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序的SQL語句緊密耦合。
如:from Student s where s.name=?
但是:
JPA僅僅是一種規范,也就是說JPA僅僅定義了一些接口,而接口是需要實現才能工作的。所以底層需要某種實現,而Hibernate就是實現了JPA接口的ORM框架。
實現Jpa中的接口需要寫大量的代碼,包括簡單的增刪改查,那可不可以由框架將這些寫好呢,于是spring data jpa出現了。
spirng data jpa是spring提供的一套簡化JPA開發的框架,不僅有接口,也有實現類,只要按照約定好的【方法命名規則】寫dao層接口,就可以在不寫接口實現的情況下,實現對數據庫的訪問和操作。同時提供了很多除了CRUD之外的功能,如分頁、排序、復雜查詢等等。
Spring Data JPA 可以理解為 JPA 規范的再次封裝抽象,底層還是使用了 Hibernate 的 JPA 技術實現。
關于在開發中到底應該使用JPA還是Mybatis爭論不休,總體來說,國外用JPA的多,國內用Mybatis的多。
Spring Data JPA是面向對象的思想,一個對象就是一個表,強化的是你對這個表的控制。spring data jpa實現了jpa(java persistence api)功能,即可以實現pojo轉換為關系型數據庫記錄的功能,通俗來講就是可以不寫任何的建表sql語句了。jpa是spring data jpa功能的一個子集。
Mybatis則是面向sql,你的結果完全來源于sql,而對象這個東西只是用來接收sql帶來的結果集。你的一切操作都是圍繞sql,包括動態根據條件決定sql語句等。mybatis并不那么注重對象的概念。只要能接收到數據就好。
各自優缺點:
面向sql就更利于優化,因為sql可以優化的點太多了。對于并發用戶多,追求性能的,Mybatis更有優勢。
面向對象就更利于移植,可維護性,因為數據對象不依賴于數據源。比如從mysql換成oracle,JPA更方便。
最終用哪個,看你們老板的要求。
Spring Boot 中使用的 Jpa 實際上是 Spring Data Jpa,在 Spring Data 中,只要你的方法名稱符合規范,它就知道你想干嘛,不需要自己再去寫 SQL。本章帶領大家學習如何在Springboot中如何集成spring data jpa
創建工程,添加 Web、Spring Data Jpa 、 MySQL 驅動依賴、lombok依賴。
? 默認Mysql驅動時8.X,如果本機安裝的Mysql是5.X,需要手動修改版本
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version> <!-- 手動添加版本號-->
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
springboot默認的連接池是HiKari,但國內用druid的也很多,所以本章中我們也用一下druid。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
常見的數據庫連接池主要有c3p0,dbcp,tomcat-jdbc-pool,druid,HiKariCP。
c3p0:不提供對數據庫的監控。使用時是單線程的。
dbcp:不提供數據庫的監控。使用時是單線程的。
tomcat jdbc pool:它兼容dbcp。但是比dbcp性能更高。
druid: 是阿里巴巴開源的數據庫連接池,提供對數據庫的監控,就是為監控而生。它的功能最為全面,可擴展性好,具有sql攔截的功能。
HiKariCP: 是數據庫連接池里面的后起之秀,出來的比較晚,但是性能很好。
總的來說:性能方面HiKariCP>druid>tomcat jdbc pool>dbcp>c3p0。
# 數據庫的基本配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/boot?characterEncoding=utf8&serverTimezone=GMT%2B8
#配置連接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# JPA配置
spring.jpa.database=mysql
# 是否在控制臺打印SQL
spring.jpa.show-sql=true
# 每次啟動項目時,數據庫初始化策略
#ddl-auto:create----每次運行該程序,沒有表格會新建表格,表內有數據會清空
#ddl-auto:create-drop----每次程序結束的時候會清空表
#ddl-auto:update----每次運行程序,沒有表格會新建表格,表內有數據不會清空,只會更新
#ddl-auto:validate----運行程序會校驗數據與數據庫的字段類型是否相同,不同會報錯
spring.jpa.hibernate.ddl-auto=update
# 指定默認的存儲引擎為InnoDB,默認情況下,自動創建表的時候會使用 MyISAM 做表的引擎,
# 如果配置了數據庫方言為 MySQL57Dialect,則使用 InnoDB 做表的引擎。
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
?
先準備數據庫表:
創建對應的實體類,添加相應注解:
package com.test.jpa.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import javax.persistence.*;
@Entity(name="users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(name="user_name")
private String userName;
private int age;
}
?
編寫Dao接口:
package com.test.jpa.dao;
import com.test.jpa.pojo.Users;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UsersRepository extends JpaRepository<Users,Integer> {
}
測試:
編寫web層測試:
總結:
通過入門案例我們可以發現,dao層只需要繼承JpaRepository接口,我們不需要寫sql語句就可以查詢出數據。這就是Jpa的方便之處。
提供了方法名稱命名查詢方式
?提供了基于@Query注解查詢與更新
? 需要自定義方法名,如下所示:
import java.util.List;
public interface UsersRepository extends Repository<Users,Integer> {
List<Users> findAll();
}
測試:
這里省掉了service層,直接在控制層測試
CrudRepository接口繼承了Repository接口
CrudRepository提供了基本的增刪改查,不再需要我們自定義。
import com.bjsxt.pojo.Users;
import org.springframework.data.repository.CrudRepository;
public interface UsersRepositoryCrudRepository extends CrudRepository<Users,Integer> {
}
該接口繼承了CrudRepository接口
該接口提供了分頁與排序的操作, 也就是該接口不用自己定義增刪改查方法和分頁排序方法
import com.bjsxt.pojo.Users;
import org.springframework.data.repository.PagingAndSortingRepository;
public interface UsersRepositoryPagingAndSorting extends PagingAndSortingRepository<Users,Integer> {
}
? 該接口繼承了PagingAndSortingRepository
? 對繼承的父接口中方法的返回值進行適配,也就是該接口不用自己定義增刪改查方法和分頁排序方法,并且讓分頁查詢更簡單。
該接口主要是提供了多條件查詢的支持,并且可以在查詢中添加排序與分頁。注意JPASpecificationExecutor是單獨存在的。不繼承上述接口。
可以看出來,因為JpaRepository繼承了前面幾個接口,所以我們重點來研究JpaRepository和JPASpecificationExecutor就可以了。
? dao層:
package com.test.jpa.dao;
import com.test.jpa.pojo.Users;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UsersJpaRepository extends JpaRepository<Users,Integer> {
}
web層測試:
package com.test.jpa.controller;
import com.test.jpa.dao.UsersJpaRepository;
import com.test.jpa.pojo.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/users")
public class UsersController {
@Autowired
private UsersJpaRepository usersJpaRepository;
@RequestMapping("/select")
public List<Users> select()
{
return usersJpaRepository.findAll();
}
@RequestMapping("/add")
public void addUser()
{
Users user=new Users();
user.setAge(19);
user.setUserName("cc");
usersJpaRepository.save(user);
}
@RequestMapping("/update")
public void updateUser()
{
Users user=usersJpaRepository.getOne(3);
user.setAge(19);
user.setUserName("ddd");
usersJpaRepository.save(user);
}
@RequestMapping("/delete")
public void deleteUserById()
{
int id=1;
usersJpaRepository.deleteById(id);
}
@RequestMapping("/selectById")
public Users selectById()
{
return usersJpaRepository.findById(3).get();//
// 2.x版本后需要.get()才能得到實體對象, id未查詢到對應實體時會報錯
}
}
//分頁查詢
@RequestMapping("/selectByPage")
public List<Users> selectByPage() {
PageRequest pageable=PageRequest.of(0, 2);
Page<Users> page=usersJpaRepository.findAll(pageable);
List<Users> usersList=page.getContent();
//數據的總條數:page.getTotalElements();
//總頁數:page.getTotalPages();
return usersList;
}
其中:
PageRequest:封裝了分頁的參數,當前頁,每頁顯示的條數。注意:它的當前頁是從0開始
Page :是spring data jpa 的分頁模型。包含的屬性信息如下:
{
"content": [{}], // 數據列表
"last": true, // 是否最后一頁
"totalPages": 1, // 總頁數
"totalElements": 1, // 數據總數
"sort": null, // 排序
"first": true, // 是否首頁
"numberOfElements": 1, // 本頁數據條數
"size": 10, // 每頁長度
"number": 0 // 當前頁序號
}
分頁并排序:
//分頁查詢并排序
@RequestMapping("/selectByPageByOrder")
public List<Users> selectByPageByOrder() {
//按照id降序排
PageRequest pageable=PageRequest.of(0, 2,Sort.Direction.DESC, "id");
Page<Users> page=usersJpaRepository.findAll(pageable);
List<Users> usersList=page.getContent();
//數據的總條數:page.getTotalElements();
//總頁數:page.getTotalPages();
return usersList;
}
JpaRepository 提供了一些基本的數據操作方法,例如保存,更新,刪除,分頁查詢等,開發者也可以在接口中自己聲明相關的方法,只需要方法名稱符合規范即可,在 Spring Data 中,只要按照既定的規范命名方法,Spring Data Jpa 就知道你想干嘛,這樣就不用寫 SQL 了,那么規范是什么呢?參考下圖:
在UsersJpaRepository接口中添加自定義方法:
List<Users> findByUserName(String userName);
List<Users> findByUserNameLike(String userName);
測試:
有的時候,Spring Data規范里提供的查詢關鍵字并不能滿足我們的查詢需求,這個時候就可以使用 @Query 關鍵字,來自定義查詢 SQL。
1.例如查詢 id 最大的 User:
@Query(value="select * from users where id=(select max(id) from users)",nativeQuery=true)
Users getMaxIdUser();
nativeQuery:代表本地查詢,就是使用原生的sql語句。
用來注入參數
@Query(value="select * from users where user_name like %:userName%",nativeQuery=true)
List<Users> findByNameMatch(@Param("userName") String userName);
?
Spring Data JPA中也有一對一、一對多、多對多映射。這些映射還分單向關聯和雙向關聯,在雙向關聯時還需要考慮對象序列化為JSON字符串時的死循環問題。
單向關聯和雙向關聯
一對一映射需要@OneToOne注解和@JoinColumn注解配合使用
準備兩張表users和cards:
創建實體類:
Cards:
package com.test.jpa.pojo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity(name="cards")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cards {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY )
private int id;
@Column(name="card_type")
private String cardType;
}
Users:
每個用戶對應一張會員卡,所以每個users對象中包含一個card對象。
package com.test.jpa.pojo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import javax.persistence.*;
import java.io.Serializable;
@Entity(name="users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(name="user_name")
private String userName;
private int age;
//@JoinColumn注解中的name元素為當前實體類中對應的屬性id,即users表中的card_id
// 而referencedColumnName則為關聯對象的id,即cards表中的主鍵id
@JoinColumn(name="card_id",referencedColumnName="id")
@OneToOne(cascade={CascadeType.ALL})
private Cards card;
}
注意:
@JoinColumn 是指表與表之間關系的字段
@OneToOne是一對一關系映射。其中CascadeType是級聯類型。
CascadeType.PERSIST
級聯新增,保存父對象時會新建其中包含的子對象
CascadeType.MERGE
級聯修改,保存父對象時會更新其中所包含的子對象數據
CascadeType.REMOVE
級聯刪除,當刪除關聯關系時會將子對象的數據刪除
CascadeType.REFRESH
級聯刷新,保存關聯關系時會更新子對象和數據庫中一致(意思是你在父對象中添加一個只包含ID的子對象,也可以保存進去)
CascadeType.ALL
包含上述所有操作
新增address表
這里一個用戶對應多個地址,是一對多的關系。
描述一對多關系中需要用到@OneToMany和@ManyToOne
首先創建Address實體類:
package com.test.jpa.pojo;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import javax.xml.soap.Detail;
@Data
//jpa中使用lombok時,需排除關聯表屬性,否則會報錯
@EqualsAndHashCode(exclude="user")
@NoArgsConstructor
@AllArgsConstructor
@Entity(name="address")
public class Address {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String detail;
@ManyToOne(cascade=CascadeType.ALL,optional=false)////可選屬性optional=false,表示user不能為空。刪除地址,不影響用戶
@JoinColumn(name="user_id")
@JsonBackReference //防止json序列化出現死循環
private Users user;
}
注意:此實體類中添加了三個新注解
@EqualsAndHashCode(exclude="user") 是指 jpa中使用lombok時,需排除關聯表屬性,否則會報錯。
@ManyToOne()代表多對一的關系
@JsonBackReference 防止json序列化出現死循環
在Users表中添加address集合
package com.test.jpa.pojo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
@Entity(name="users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(name="user_name")
private String userName;
private int age;
//@JoinColumn注解中的name元素為當前實體類中對應的屬性id,即users表中的card_id
// 而referencedColumnName則為關聯對象的id,即cards表中的主鍵id
@JoinColumn(name="card_id",referencedColumnName="id")
@OneToOne(cascade={CascadeType.ALL})
private Cards card;
@OneToMany(mappedBy="user")
private List<Address> addressList;
}
注意:這里新增了一個注解
@OneToMany(mappedBy="user") 代表一對多的關系
其中屬性mappedBy的意思是:
1.只有OneToOne,OneToMany,ManyToMany上才有mappedBy屬性,ManyToOne不存在該屬性,在@OneToMany里加入mappedBy屬性可以避免生成一張中間表。
2.mappedBy標簽一定是定義在被擁有方的,他指向擁有方; 表示聲明自己不是一對多的關系維護端,由對方來維護,是在一的一方進行聲明的。mappedBy的值應該為一的一方的表名
3.mappedBy的含義,應該理解為,擁有方能夠自動維護跟被擁有方的關系,當然,如果從被擁有方,通過手工強行來維護擁有方的關系也是可以做到的;
4.mappedBy跟joinColumn/JoinTable總是處于互斥的一方,可以理解為正是由于擁有方的關聯被擁有方的字段存在,擁有方才擁有了被擁有方。mappedBy這方定義JoinColumn/JoinTable總是失效的,不會建立對應的字段或者表。
測試:
準備任務表和用戶和任務的關系表,一個用戶對應多個任務,一個任務對應多個用戶,形成多對多的關系.
創建實體類:Tasks
package com.test.jpa.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name="tasks")
public class Tasks {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String content;
}
修改Users實體類,添加多對多關系
package com.test.jpa.pojo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
@Entity(name="users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@Column(name="user_name")
private String userName;
private int age;
//@JoinColumn注解中的name元素為當前實體類中對應的屬性id,即users表中的card_id
// 而referencedColumnName則為關聯對象的id,即cards表中的主鍵id
@JoinColumn(name="card_id",referencedColumnName="id")
@OneToOne(cascade={CascadeType.ALL})
private Cards card;
@OneToMany(mappedBy="user")
private List<Address> addressList;
@ManyToMany
@JoinTable(name="user_task",joinColumns=@JoinColumn(name="user_id"),
inverseJoinColumns=@JoinColumn(name="task_id"))
private List<Tasks> tasksList;
}
注意:這里新增了兩個注解
@ManyToMany 代表多對多的關系
@JoinTable 存放的是兩個實體間的多對多關系表,name中存放的是關系表表名, joinColumns存放的當前實體類在關心表中的id名,inverseJoinColumns存放的是關聯的實體表在關系表中的id名.
測試:
? 該接口主要是提供了多條件查詢的支持,并且可以在查詢中添加排序與分頁。它是獨立存在的。
? 多條件查詢示例:
添加dao層
package com.test.jpa.dao;
import com.test.jpa.pojo.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface UsersSpecificationExecutor extends JpaRepository<Users,Integer>,JpaSpecificationExecutor<Users> {
}
控制層測試:
package com.test.jpa.controller;
import com.test.jpa.dao.UsersSpecificationExecutor;
import com.test.jpa.pojo.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.swing.text.html.HTMLDocument;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/specification")
public class TestSpecificationExecutor {
@Autowired
private UsersSpecificationExecutor usersSpecificationExecutor;
@RequestMapping("/select")
public List<Users> select()
{
Specification<Users> usersSpecification=new Specification<Users>(){
//CriteriaBuilder是一個工廠類,用來創建安全查詢的criteriaQuery對象和拼接的查詢條件
//Root根對象對應于from后面的表
//Predicate用于查詢條件的拼接,對應于where后面的表達式。
@Override
public Predicate toPredicate(Root<Users> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> list=new ArrayList<>();
list.add(criteriaBuilder.equal(root.get("userName"),"aa"));
list.add(criteriaBuilder.equal(root.get("age"),11));
Predicate[] arr=new Predicate[list.size()];
return criteriaBuilder.and(list.toArray(arr));
}
};
List<Users> list=this.usersSpecificationExecutor.findAll(usersSpecification);
return list;
}
}
? JPQL全稱Java Persistence Query Language。中文意思是Java持久化查詢語言。
? 是一種可移植的查詢語言,旨在以面向對象表達式語言的表達式,將SQL語法和簡單查詢語義綁定在一起·使用 這種語言編寫的查詢是可移植的,可以被編譯成所有主流數據庫服務器上的SQL。
其特征與原生SQL語句類似,并且完全面向對象,通過類名和屬性訪問,而不是表名和表的屬性。
查詢用的 SELECT 語法
比如:SELECT u FROM Users u WHERE u.userName=:userName
? 對應sql: select * from users where user_name=參數
解析: 這里的Users是指實體 u是別名。u.userName是實體對象中的userName屬性。:userName是傳遞的參數。
更新用的 UPDATE 語法
比如:UPDATE Users u SET u.userName=:userName where u.id=:id
刪除用的 DELETE 語法
比如: delete from Users u where u.id=:id
注意:JPQL中沒有insert添加語句
創建接口UsersDao3,添加查詢方法.
package com.test.springdatajpa.dao;
import com.test.springdatajpa.pojo.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface UsersDao3 extends JpaRepository<Users,Integer> {
//查詢
@Query("SELECT u FROM users u WHERE u.userName=:userName")
//簡化寫法
//@Query(" FROM users u WHERE u.userName=:userName")
public List<Users> getUsers(@Param("userName") String userName);
}
注意:
@Query(“SELECT u FROM users u WHERE u.userName=:userName”) 中的users是實體名,而實體名用哪個有兩種情況:
情況1:在實體類中直接用@Entity(name=“users”) 不用@Table ,則表示此實體對應的表名是users,同時實體名也設置為users。如下:
情況2:在實體類中用
@Entity
@Table(name=“users”) 兩個注解,則表示此實體對應的表名是users,實體名為類名。如下:
調用dao層方法測試:
在UsersDao3中添加修改和刪除:
package com.test.springdatajpa.dao;
import com.test.springdatajpa.pojo.Users;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface UsersDao3 extends JpaRepository<Users,Integer> {
//查詢
//@Query("SELECT u FROM users u WHERE u.userName=:userName")
@Query(" FROM users u WHERE u.userName=:userName")
public List<Users> getUsers(@Param("userName") String userName);
//修改
//SpringData 的每個方法上有事務, 但都是一個只讀事務。 他們不能完成修改操作,所以需要手動添加@Transactional
@Transactional
@Query( "UPDATE users u SET u.userName=:userName where u.id=:id")
@Modifying
public void updateUsers(@Param("id") int id, @Param("userName") String userName);
//刪除
@Transactional
@Query( "delete from users u where u.id=:id")
@Modifying
public void deleteUsers(@Param("id") int id);
}
注意:
在使用修改和刪除語句的時候。需要加@Modifying和@Transactional注解
@Transactional:是因為SpringData 的每個方法上有事務, 但都是一個只讀事務。 他們不能完成修改操作,所以需要手動添加
@Modifying:JPQL實現DELETE和UPDATE操作的時候必須加上@modifying注解,以通知Spring Data 這是一個DELETE或UPDATE操作。
呼~,歷過好幾天的奮戰終于把集合框架肝完了,b站某馬老師講的是真的非常詳細而且動聽,原理給你分析得明明白白的,此前也找了許多關于集合這一大章節的視頻,發現更多的是針對于使用,原理講的并不是很多,這就導致我在練習或者回顧時還是一知半解。以下是我結合視頻以及個人的一些理解和體會做的筆記總結。路漫漫其修遠兮,吾將上下而求索,希望這篇總結對你有些許幫助,嘻嘻!
集合:Java中提供的一種容器,可以用來存儲多個數據。java集合大致可以分為Set,List,Queue和Map四種體系。
數組和集合的區別:
單列集合體系結構:
Collection接口是所有單列集合的父接口,因此在單列集合中定義的List和set通用的一些方法,這些方法可以操作所有的單列集合。方法如下:
【參考代碼】
package Collection; import java.util.ArrayList; import java.util.Collection; /* Collection集合常用方法 boolean add(E e); 向集合中添加元素 boolean remove(E e); 刪除集合中的某個元素 void clear(); 清空集合中所有的元素 boolean contains(); 判斷集合中是否含有xxx元素 boolean isEmpty(); 判斷集合是否為空 int size(); 計算集合的長度 Object[] toArray(); 將集合轉成一個數組 */ public class Test { public static void main(String[] args) { //創建集合對象 , 可以多態使用 Collection<String>col=new ArrayList<>(); // Collection<String>col=new HashSet<>(); 下面的功能照樣能實現:共性方法 col.add("小明"); // 添加元素 col.add("小紅"); col.add("小藍"); col.add("小綠"); System.out.println(col); //[小明, 小紅, 小藍, 小綠] //boolean remove(E e); 刪除集合中的某個元素 // boolean ans=col.remove("小明"); // System.out.println(ans);//true // System.out.println(col);//[小紅, 小藍, 小綠] //void clear(); 清空集合中所有的元素 // col.clear(); // System.out.println(col);//[] //boolean contains(); 判斷集合中是否含有xxx元素 // boolean result=col.contains("小明"); // System.out.println(result);//true //boolean isEmpty(); 判斷集合是否為空 // boolean result=col.isEmpty(); // System.out.println(result);// 不為空false //int size(); 計算集合的長度 // int len=col.size(); // System.out.println(len);// 4 //Object[] toArray(); 將集合轉成一個數組 Object[] arr=col.toArray(); // 遍歷數組 // for (int i=0; i < arr.length; i++) { // System.out.println(arr[i]); // } } }
引入:由于集合有多種,每種集合存儲跟讀取的方式都不一樣,好比衣柜、水瓶、藥瓶,你存和取的方式肯定不一樣。如果每種集合都定義一種遍歷方式那將十分的繁瑣。
迭代器(Iterator):它不是一個容器而是接口,它是一種用于訪問容器的方法,可用于迭代 List、Set和Map等容器。
迭代:即Collection集合的通用獲取方式。再獲取元素之前先要判斷集合中是否有元素,如果有就將這個元素去取出來,繼續再判斷,直到集合所有元素被取出來為止。即:一個一個的往外拿。
作用:幫我們遍歷或者拿到容器里邊的數據。
迭代器常用操作:
迭代器的使用步驟:
【參考代碼】
package Iterator; import javax.swing.text.html.parser.Entity; import java.util.*; public class Test { public static void main(String[] args) { //創建一個集合對象 Collection<String>col=new ArrayList(); //添加元素 col.add("小明"); col.add("小紅"); col.add("小藍"); col.add("小綠"); /* 1.使用集合的方法iterator()獲取迭代器的實現類對象,使用Iterator接口接收(多態) 注意: Iterator接口也是有泛型的,迭代器的泛型跟集合走,集合是什么泛型,迭代器就是什么泛型 */ // 多態 接口 實現類對象 Iterator<String>it=col.iterator(); // 2.使用 Iterator接口中的hashNext方法判斷是否還有下一個元素 while(it.hasNext());{ // 3.使用 Iterator接口中的next方法取出集合的下一個元素 String str=it.next(); System.out.println(str); } } }
增強for循環(for each循環)是JDk1.5之后的一個高循環,專門用來遍歷數組和集合的,所有的數組跟單列集合都可以使用。它的內部原理就是一個迭代器Iterator,所以在遍歷過程中,不能對集合元素進行增刪操作。
語法:
for(類型 變量 : 數組/集合){// 數組或者集合里的每一項賦值給這個變量
// 循環體
}
【參考代碼】
String[] student={"小明","小紅","小藍"}; // // 傳統遍歷方式 // for (int i=0; i < student.length; i++) { // System.out.println(student[i]); // } // 增強for for(String c : student){ System.out.println(c); } -------------------------------- List<Integer>list=new ArrayList<Integer>(); list.add(123); list.add(234); list.add(456); for(Integer n : list){ System.out.println(n); }
注:增強for必須有被遍歷的目標。目標只能是數組或者Collection,而它僅僅作為遍歷操作實現
在前面學習集合時,我們知道集合時可以存放任意對象的,只要把對象存儲集合后,它們都會被向上轉型提升為Object類型。當我們要取出這些對象時必須進行類型強制轉換,由Object類型變為原來的類型。
不使用泛型:
- 好處:集合默認類型是Object類,可以存儲任意類型的數據
- 弊端:不安全,會引發異常,需要強轉。
public static void main(String[] args) { List list=new ArrayList(); list.add("小明"); list.add("小紅"); for (int i=0; i < list.size(); i++) { String s=(String)list.get(i) // 強轉 System.out.println(s); } }
使用泛型:
public static void main(String[] args) { List<String> list=new ArrayList();// 規范了數據類型,只能放字符串! list.add("小明"); list.add("小紅"); //stringList.add(123);// 除了字符串以外的類型不能加,報錯! for (int i=0; i < list.size(); i++) { String s=list.get(i); // 不用再強轉了 System.out.println(s); } }
在上述的實例中,我們只能添加String類型的數據,否則編譯器會報錯。
定義格式:
修飾符 class 類名<泛型變量>{ } // 注:泛型變量建議使用E、T、K、V
例如:
public class Box<T> {
private T t;
public void add(T t) {
this.t=t;
}
public T get() {
return t;
}
參考示例:
注:在創建對象時確定泛型的類型
定義格式:
修飾符 <泛型變量> 返回值的類型 方法名稱(形參列表){
//方法體
}
注:含有泛型的方法,在調用的時候確定泛型的數據類型
傳遞什么類型的參數,泛型就是什么類型
參考示例:
定義格式:
public interface 接口名<泛型類型> {
}
使用方式1:定義接口的實現類,實現接口,并且指定接口的泛型
使用方式2:接口使用什么泛型,實現類就使用什么泛型,類跟著接口走。
就相當于定義了一個含有泛型的類,創建對象的時候確定泛型的類型。
下圖接口同上圖接口
當使用泛型類或接口時,傳遞數據中,泛型類型不確定,可以通過通配符表示<?>表示。但一旦使用泛型的通配符后,只能使用Object類中的共性方法,集合中元素自身方法無法使用。
泛型的通配符:不知道使用什么類型來接收的時候,此時可以使用 ? ,?表示未知通配符
此時只能接收數據,不能往集合中存儲數據。
【參考代碼】
package FanXing; import javax.swing.text.html.HTMLDocument; import java.util.ArrayList; import java.util.Iterator; /* 泛型的通配符: ?:代表數據類型 使用方式: 不能在創建對象時使用 只能作為方法的傳遞參數使用 */ public class Generic { public static void main(String[] args) { ArrayList<Integer>list01=new ArrayList<>(); list01.add(123); list01.add(456); ArrayList<String>list02=new ArrayList<>(); list02.add("小明"); list02.add("小紅"); // ......還有很多其它類型 printArray(list01); printArray(list02); /* 定義一個方法,能遍歷所有類型的ArrayList集合 這時候我們不知道ArrayList集合使用的是什么類型,可以使用泛型的通配符:?來代表數據類型 注意:泛型沒有繼承的概念 */ } public static void printArray(ArrayList<?>list){ // 使用迭代器遍歷集合 Iterator<?> it=list.iterator(); while(it.hasNext()){ Object obj=it.next();//it.next()取出的元素是Object類。Object類 可以接收任意的數據類型 System.out.println(obj); } } }
之前設置泛型的時候,實際上是可以可以任意設置的,只要是類就可以設置。但在Java的泛型中可以指定一個泛型的上限和下限。
泛型的上限:
泛型的下限:
比如:Object類、String類、Number類、Integer類,其中Number類是Integer的父類。
集合是基于數據結構做出來的,不同的集合底層采用不同的數據結構。不同的數據結構,功能和作用是不一樣的。
數據結構是指數據以什么方式組織在一起。不同的數據結構,增刪查的性能是不一樣的。
棧:stack,又稱堆棧,它是運算受限的線性表,只能在棧的受限一端進行插入和刪除操作。
特點:先進后出
隊列:queue,簡稱隊,它同棧由于也是運算受限的線性表,只能在表的一端進行插入操作,而在表的另一端進行刪除操作。
特點:先進先出
數組:Array,是個有序的元素序列,數組在內存中開辟一段連續的空間。
特點:
鏈表:linked list,由一系列結點node組成,結點可以在運行時動態產生。每個節點包含兩個部分:數據域(data)和指向下一個節點的指針域(next)。鏈表包括單鏈表和雙向鏈表。
特點:
紅黑樹:R-B Tree,全稱是Red-Black Tree,又稱為“紅黑樹”,它一種特殊的二叉查找樹。紅黑樹的每個節點上都有存儲位表示節點的顏色,可以是紅(Red)或黑(Black),它是一種弱平衡二叉樹(Weak AVL)。
特點:
(1)每個節點或者是黑色,或者是紅色。 (2)根節點是黑色。 (3)每個葉子節點(NIL)是黑色。 [注意:這里葉子節點,是指為空(NIL或NULL)的葉子節點!] (4)如果一個節點是紅色的,則它的子節點必須是黑色的。 (5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
注:以上數據結構可以結合所學過c語言數據結構
List集合體系:添加元素,是有序,可重復,有索引的,大小可變。實際開發中常用的是ArrayList集合。List集合體系包括以下幾種:
List集合繼承了Collection集合的全部功能,同時因為List集合系列有索引,所以多了很多按照索引操作元素的方法:
add(int index, E element) 根據索引添加元素
get(int index) 根據索引獲取元素
remove(int index) 根據索引刪除元素
set(int index, E element) 根據索引修改該位置上的元素
contains(E element)判斷容器是否含有XXX東西
clear() 清空集合中的元素
size()計算集合的大小
【參考代碼】
package Collection; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class TestList { public static void main(String[] args) { List<String>list=new ArrayList(); // 換成Linkedist 下面的操作都能一樣實現 list.add("小明"); list.add("小紅"); list.add("小藍"); list.add("小綠"); list.add("小明"); // // 在某個索引位置往集合中添加元素 // list.add(2,"哈哈哈哈"); // System.out.println(list); // // 刪除集合中某個元素 // list.remove("小藍"); // // 根據索引獲取元素 // System.out.println(list.get(0)); // // 修改索引位置處的元素 // list.set(0,"小明很明白!"); // System.out.println(list.get(0));//小明很明白! // // 計算列表的大小(長度): // System.out.println(list.size()); // //判斷列表中是否有xxx false // System.out.println(list.contains("小藍")); } }
ArrayList集合存儲的結構是數組結構,元素增刪慢,查詢快。最常用。
LinkedList集合存儲的結構是鏈表結構,方便元素的添加、刪除操作。LinkedList是一個雙向鏈表
LinkedList的特點:
實際開發中對一個集合的添加、刪除操作經常涉及首尾操作,LinkedList提供了很多操作首尾元素方法
public void addFirst(E e); 將指定的元素插到列表開頭。 public void addLat(E e); 將指定的元素插到列表結尾。 此方法等效于add()方法 public void push(E e); 將元素推入此列表所示的堆棧。 此方法等效于addFirst()方法 public E getFirst(); 返回此列表的第一個元素 public E getLast(); 返回此列表的最后一個元素 public E removeFirst(); 移除并返回此列表的第一個元素 public E removeLast(); 移除并返回此列表的最后一個元素 public E pop(E e); 入此列表所示的堆棧中彈出一個元素。 public boolean isEmpty(); 如果列表為空 返回true
【參考代碼】
package Collection; /* public void addFirst(E e); 將指定的元素插到列表開頭。 public void addLast(E e); 將指定的元素插到列表結尾。 public void push(E e); 將元素推入此列表所示的堆棧。 public E getFrist(); 返回此列表的第一個元素 public E getLast(); 返回此列表的最后一個元素 public E removeFrist(); 移除并返回此列表的第一個元素 public E removeLast(); 移除并返回此列表的最后一個元素 public E pop(E e); 入此列表所示的堆棧中彈出一個元素。 public boolean isEmpty(); 如果列表為空 返回true */ import java.util.LinkedList; import java.util.List; public class TestLinkedList { public static void main(String[] args) { show01(); show02(); show03(); } /* public void addFirst(E e); 將指定的元素插到列表開頭。 public void addLast(E e); 將指定的元素插到列表結尾。 public void push(E e); 將元素推入此列表所示的堆棧 */ public static void show01(){ // 注:LinkedList特有的方法不能使用多態! // List<String> list=new LinkedList<>(); 是不對的 LinkedList<String>list=new LinkedList<>(); // add()添加元素 list.add("a"); list.add("b"); list.add("c"); System.out.println(list);//[a, b, c] list.addFirst("hhh"); //public void push(E e); 將元素推入此列表所示的堆棧。 等效于addFirst() list.push("hhh"); System.out.println(list); //public void lastFrist(E e); 將指定的元素插到列表結尾。 等效于add() list.addLast("com"); System.out.println(list); } /* public E getFrist(); 返回此列表的第一個元素 public E getLast(); 返回此列表的最后一個元素 */ public static void show02(){ LinkedList<String>list=new LinkedList<>(); // add()添加元素 list.add("a"); list.add("b"); list.add("c"); // list.clear(); // 清空集合中所有元素 if(! list.isEmpty()){ System.out.println(list.getFirst());//a System.out.println(list.getLast());//c } } /* public E removeFrist(); 移除并返回此列表的第一個元素 public E removeLast(); 移除并返回此列表的最后一個元素 public E pop(E e); 入此列表所示的堆棧中彈出一個元素。 */ public static void show03(){ LinkedList<String>list=new LinkedList<>(); // add()添加元素 list.add("a"); list.add("b"); list.add("c"); System.out.println(list.pop()); //public E pop(E e); 入此列表所示的堆棧中彈出一個元素。 等效于 removefirst() //System.out.println(list.pop()); System.out.println(list.removeFirst());//a System.out.println(list.removeLast());//c System.out.println(list);//[b] } }
注:使用LinkedList集合特有的方法,不能使用多態。
數組結構實現,查詢快,增刪慢;
JDK1.0版本,運行效率慢、線程安全
【參考代碼】
package Collection; import javax.swing.text.html.HTMLDocument; import java.util.Enumeration; import java.util.Iterator; import java.util.Vector; /* Vector集合的使用 存儲結構:數組 */ public class VectorTest { public static void main(String[] args) { // 創建集合 Vector<String>vector=new Vector<>(); // 添加元素 vector.add("小明"); vector.add("小紅"); vector.add("小藍"); System.out.println("元素個數"+ vector.size()); // 判斷 System.out.println(vector.contains("小明")); System.out.println(vector.isEmpty()); //刪除 vector.remove("小紅"); System.out.println(vector); //清空 clear(); vector.clear(); // 遍歷 Iterator<String> it=vector.iterator(); while (it.hasNext()){ String str=it.next(); System.out.println(str); } //vector獨有的遍歷 使用枚舉器 // Enumeration<String>en=vector.elements(); // while (en.hasMoreElements()){ // String s=en.nextElement(); // System.out.println(s); // } } }
Set系列集合:添加的元素,是無序的,不重復的,無索引的(索引的操作不能用)。
——HashSet:添加的元素,是無序的,不重復的,無索引的。
——LinkedHashSet:添加的元素,是有序的,不重復的,無索引的。
——TreeSet:不重復,無索引,按照大小默認升序排序!!(可排序集合)
遍歷方式:由于Set集合五索引,故沒有for循環遍歷,只有三種遍歷。
注:存儲的字符串,Integer等類型的數據,它們是Java已經定義好了類,它們都重寫了hashCode方法和equals方法,保證了元素的唯一性!
HashSet 保證元素唯一性的原理
我們使用 Set 集合都是需要去掉重復元素的, 如果在存儲的時候逐個 equals() 比較, 效率較低,哈希算法提高了去重復的效率, 降低了使用 equals() 方法的次數。
當 HashSet 調用 add() 方法存儲對象的時候, 先調用對象的 hashCode() 方法得到一個哈希值, 然后在集合中查找是否有哈希值相同的對象,如果沒有哈希值相同的對象就直接存入集合。如果有哈希值相同的對象, 就和哈希值相同的對象逐個進行 equals() 比較,比較結果為 false 就存入, true 則不存。存儲元素必需要重寫HashCode方法和equals方法
給HashSet中存放自定義的類型時,必需要重寫HashCode方法和equals方法,建立自己的比較方式,才能保證HashSet集合中對象的唯一性!
【參考代碼】
Person類:
package Collection; import java.util.Objects; public class Person { private String name; private int age; public Person(String name, int age) { this.name=name; this.age=age; } // 用于打印 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } // 重寫hashCode方法和equals方法 @Override public boolean equals(Object o) { if (this==o) return true; if (o==null || getClass() !=o.getClass()) return false; Person person=(Person) o; return age==person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } public String getName() { return name; } public void setName(String name) { this.name=name; } public int getAge() { return age; } public void setAge(int age) { this.age=age; } }
// 主控制臺 package Collection; import java.util.HashSet; import java.util.Set; /* HashSet存儲自定義類型的元素 Set集合保證元素唯一性: 存儲的元素(String Integer,...Student,Person...) 必須重寫hashCode方法和equals方法 要求: 同名且同年齡視為同一個人噢 */ public class TestHaxhSet { public static void main(String[] args) { // 創建hashSet集合存儲Person Set<Person>set=new HashSet<>(); //集合類存放對象的! // 創建對象(人) /* // 在沒有重寫hashCode方法和equals方法前,它們的哈希值都是不一樣的,equals也為false 故沒有重復 Person p1=new Person("小明",18); Person p2=new Person("小明",19); Person p3=new Person("小明",18); System.out.println(p1.hashCode());// 460141958 System.out.println(p2.hashCode());// 1163157884 System.out.println(p3.hashCode());// 1956725890 System.out.println(p1.equals(p2));// false set.add(p1); set.add(p2); set.add(p3); System.out.println(set);// [Person{name='小明', age=18}, Person{name='小明', age=19}, Person{name='小明', age=18}] */ // 重寫hashCode方法和equals方法之后set對象就唯一性了 Person p1=new Person("小明",18); Person p2=new Person("小明",19); Person p3=new Person("小明",18); set.add(p1); set.add(p2); set.add(p3); System.out.println(set);// [Person{name='小明', age=19}, Person{name='小明', age=18}] } }
我們知道HashSet保證元素的唯一性,但存放進去的元素是無序的,那我們要保證有序,該怎么辦好呢?
在HashSet下面的一個子類Java.util.LinkedHashSet。它是鏈表和哈希表組合的一個數據結構。
LinkedHashSet集合的特點:
底層是一個哈希表(數組+鏈表/紅黑樹)+鏈表:多了一條鏈表(記錄元素的存儲順序),保證元素有序
具有可預知迭代順序的 Set 接口的哈希表和鏈接列表實現,即按照將元素插入到 set 中的順序(插入順序)進行迭代。
HashSet與LinkedHashSet的區別:
【參考代碼】
package Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; public class TestHashSet { public static void main(String[] args) { Set<String>set=new HashSet<>(); set.add("kkk"); set.add("abc"); set.add("abc"); set.add("afterglow"); System.out.println(set);//[afterglow, abc, kkk] 無序,不重復 Set<String>Linkset=new LinkedHashSet<>(); Linkset.add("kkk"); Linkset.add("abc"); Linkset.add("abc"); Linkset.add("afterglow"); System.out.println(Linkset);//[kkk, abc, afterglow] 有序,不重復 } }
使用前提:
如果我們定義一個方法需要接收多個參數,并且多個參數類型一致,我們可以對其做如下格式的簡化:
修飾符 返回值類型 方法名(參數類型... 形參名){ }
這個寫法完全等價于:
修飾符 返回值類型 方法名(參數類型[] 形參名){ } ,
后者在調用時必須傳遞數組,而前者可以直接傳遞數據類型。
可變參數原理:
可變參數底層是一個數組,根據參數個數不同,會創建不同長度的數組來存儲這些參數。傳遞參數的個數,可以是0~n個
【參考代碼】
package Collection; public class KeBiancanShu { public static void main(String[] args) { int i=add(1,2,3,4); System.out.println(i); } // // 兩個數的和 // public static int add(int a, int b){ // return a + b; // } // // 三個數的和,要是多個一直往下寫,很麻煩! // public static int add(int a, int b, int c){ // return a + b +c; // } /* 求0~n個整數的和 數據類型已經確定:int 參數個數不確定,可以使用可變參數 */ public static int add(int...arr){ // System.out.println(arr);// [I@1b6d3586 底層是一個數組 // System.out.println(arr.length);// 可變數組的長度,卻決于你添加的個數 int sum=0; for (int i : arr){ sum +=i; } return sum; } }
注意事項:
【示例代碼】
/*
可變參數注意事項:
一個方法的參數列表,只能有一個可變參數
如果方法的參數有多個,那么可變參數必須寫在參數列表的末尾!
*/
//一個方法的參數列表,只能有一個可變參數
// public static void method01(int...a,String...b){ 報錯!
// }
//如果方法的參數有多個,那么可變參數必須寫在參數列表的末尾!
public static void method02(String b, double c, int...a){
}
【參考代碼】
public class Test { public static void main(String[] args) { List<Integer>list=new ArrayList<Integer>(); list.add(120); list.add(20); list.add(220); // 求最值 Integer max=Collections.max(list); System.out.println(max); Integer min=Collections.min(list); System.out.println(min); // 排序 Collections.sort(list); System.out.println(list); // 打亂順序 Collections.shuffle(list); // 斗地主發牌 System.out.println(list); // 不定參數添加 Collections.addAll(list,456,789); System.out.println(list);//[220, 20, 120, 456, 789] } }
注意:
sort(List<T> list)使用前提:
排序的集合里邊存儲的元素,必須實現Comparable接口,重寫接口中的方法compareTo定義排序的規則。在Java中Integer、String等等數據類型已經幫我們實現Comparable接口并重寫接口中的方法compareTo了。如果要對自己定義的類進行排序,我們就要自己實現接口并重寫compareTo然后進行自定義排序規則。
Comparable接口的排序規則:
自己(this) - 參數:升序,反之降序
【示例參考】:比較自定義類型
輸出結果:
[Student{name='小明', age=18}, Student{name='小紅', age=20}, Student{name='小藍', age=19}] [Student{name='小明', age=18}, Student{name='小藍', age=19}, Student{name='小紅', age=20}]
sort(List< T > list , Comparator<? super T >)的使用:
Comparator:相當于找一個第三放的裁判,按照Comparator比較器里面重寫的compare方法對元素進行排序比較
Comparator的比較規則:
o1 - o2 升序
【參考代碼】
public class TestComparator { public static void main(String[] args) { List<Integer> list=new ArrayList<>(); list.add(2); list.add(1); list.add(3); Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1 - o2;// 升序 // return o2 - o1;// 降序 } }); System.out.println(list);// [1, 2, 3] } }
【示例參考】:比較自定義類型
Map集合的特點
注:映射由Map<K,V>接口的實例表示,它不是繼承自Collection接口。
Map系列集合,常用子類的包括:
——HashMap
——LinkedHashMap
【HashMap集合】
java.util.HashMap<k , v >集合implements Map<k , v>接口.
HashMap集合的特點:
【LinkedHashMap集合】
java.util.LinkedHashMap<k , v >集合extends HashMap<k , v>集合。
LinkedHashMap集合的特點:
Map接口中定義了很多方法,常見如下:
【參考代碼】
package Map; import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { // 創建Map集合對象,多態 Map<Integer,String>map=new HashMap(); map.put(11,"小明"); map.put(22,"小紅"); map.put(33,"小藍"); map.put(44,"小綠"); System.out.println(map);// {33=小藍, 22=小紅, 11=小明, 44=小綠} HashMap無序的 map.remove(44);// 刪除 System.out.println(map);// {33=小藍, 22=小紅, 11=小明} System.out.println(map.size()); //大小 3 System.out.println(map.containsKey(33)); //true System.out.println(map.containsValue("小藍")); //true map.put(22,"小芳"); // {33=小藍, 22=小芳, 11=小明} 若出現重復的key原來的數據會被頂替 System.out.println(map); // map.put(55,"小明"); // System.out.println(map);//是否被頂替卻決于key,key映射value,而不是value映射key {33=小藍, 22=小芳, 55=小明, 11=小明} System.out.println(map.keySet()); // [33, 22, 11] 把map中的key打包成Set集合的形式 System.out.println(map.get(33));// 小藍 通過key查詢value } }
方法一:通過鍵找值的方式
【參考代碼】
package Map; import javax.swing.text.html.HTMLDocument; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { // 創建Map集合對象 Map<String,Integer>map=new HashMap<>(); map.put("小明",18); map.put("小紅",18); map.put("小藍",19); map.put("小綠",20); //1. 使用Map集合中的方法keySet(),把Map集合里所有的key取出來,存放到一個Set集合中\ Set<String> set=map.keySet(); //2.遍歷set集合,獲取Map集合中的每一個key /* 使用while遍歷 */ Iterator <String> it=set.iterator(); while (it.hasNext()){ String key=it.next(); //3.通過Map集合中的get(key)方法,找到value Integer value=map.get(key); System.out.println(key+"="+value); } System.out.println("-----------------------"); /* 使用增強for遍歷 */ for(String key : set){ //3.通過Map集合中的get(key)方法,找到value Integer value=map.get(key); System.out.println(key+"="+value); } } }
【總結】:
while——迭代器遍歷:
Set<String> set=map.keySet();
Iterator <String> it=set.iterator();
while (it.hasNext()){
String key=it.next();
Integer value=map.get(key);
System.out.println(key+"="+value);
}
增強for遍歷:
Set<String> set=map.keySet();
for(String key : set){
//3.通過Map集合中的get(key)方法,找到value
Integer value=map.get(key);
System.out.println(key+"="+value);
}
方法二:鍵值對的方式遍歷(更加面向對象)
把鍵值對當成一個整體遍歷,增強for無法遍歷,這個整體不是類型,因此Java提供了方法:
Map集合通過代碼Set<Map.Entry<K,V>> ,將鍵值對元素轉成了一個實體類型,此時得到的是一個Entry對象,類型是:Map.Entry<K,V>
【參考代碼】
package Map; import javax.swing.text.html.HTMLDocument; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class Test { public static void main(String[] args) { // 創建Map集合對象 Map<String,Integer>map=new HashMap<>(); map.put("小明",18); map.put("小紅",18); map.put("小藍",19); map.put("小綠",20); //1.通過Map集合中的entrySet()方法,把Map集合中的多個Entry對象取出來,存儲到一個Set集合中 Set<Map.Entry<String,Integer>> set=map.entrySet(); //遍歷set集合,獲取每一個Entry對象 //使用迭代器遍歷set集合 Iterator <Map.Entry<String,Integer>> it=set.iterator(); while (it.hasNext()){ Map.Entry<String,Integer>entry=it.next(); // 使用Entry對象中的getKey()和getValue()方法獲取鍵和值 String key=entry.getKey(); Integer value=entry.getValue(); System.out.println(key+"="+value); } System.out.println("-----------"); //增強for for(Map.Entry<String,Integer> entry : set){ // 使用Entry對象中的getKey()和getValue()方法獲取鍵和值 String key=entry.getKey(); Integer value=entry.getValue(); System.out.println(key+"="+value); } } }
【總結】:
while——迭代器遍歷:
Set<Map.Entry<String,Integer>> set=map.entrySet(); //遍歷set集合,獲取每一個Entry對象 //使用迭代器遍歷set集合 Iterator <Map.Entry<String,Integer>> it=set.iterator(); while (it.hasNext()){ Map.Entry<String,Integer>entry=it.next(); // 使用Entry對象中的getKey()和getValue()方法獲取鍵和值 String key=entry.getKey(); Integer value=entry.getValue(); System.out.println(key+"="+value); }
增強for遍歷:
//增強for
for(Map.Entry<String,Integer> entry : set){
// 使用Entry對象中的getKey()和getValue()方法獲取鍵和值
String key=entry.getKey();
Integer value=entry.getValue();
System.out.println(key+"="+value);
}
Entry:表示一個key和value,它提供了獲取對應key和value的方法:
public K getKey():獲取Entry中的key
public V getValue():獲取Entry中的value
方法二圖解:
練習:每位學生(姓名,年齡)都有自己的家庭住址。那么,既然有對應關系,則將學生對象和家庭住址存儲到map集合中,學生作為鍵,地址為值。
注:學生姓名、年齡相同則視為同一人
package Map; /* hashMap存儲自定義類型鍵值: Map集合保證key是唯一的: 作為key元素,必須重寫hashMap方法和equals方法,以保證key唯一 */ import java.util.HashMap; import java.util.Set; public class HashMapSavePerson { public static void main(String[] args) { show01(); /* 上海-->HashMapSavePerson{name='小藍', age=18} 深圳-->HashMapSavePerson{name='小綠', age=18} 北京-->HashMapSavePerson{name='小紅', age=18} key唯一 */ } /* hashMap存儲自定義類型鍵值: key:String類型 String類重寫hashCode方法和equals方法,可以保證key唯一 value:Person類型 value可以重復(同名同年齡視為重復) */ public static void show01(){ // 創造HashMap集合 HashMap<String,Person> map=new HashMap<>(); //往集合中添加元素 map.put("深圳",new Person("小明",18)); map.put("上海",new Person("小藍",18)); map.put("北京",new Person("小紅",18)); map.put("深圳",new Person("小綠",18)); // 使用keySet()增強for遍歷map集合 Set<String> set=map.keySet(); for(String key:set){ Person value=map.get(key); System.out.println(key+"-->"+value); // 因為字符串類(Java幫我們的)重寫了hashCode方法和equals方法,所以鍵(key)是不能重復的 } } }
Person類:
下面這個是我們自己定義的key的類型,Person類,上面例子的是String類:
package Map; /* hashMap存儲自定義類型鍵值: Map集合保證key是唯一的: 作為key元素,必須重寫hashMap方法和equals方法,以保證key唯一 */ import javax.swing.text.html.HTMLDocument; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class HashMapSavePerson { public static void main(String[] args) { show02(); } /* hashMap存儲自定義類型鍵值: key:Person類型 Person就必須類重寫hashCode方法和equals方法,來保證key唯一 value:String類型 value可以重復(同名同年齡視為重復) */ public static void show02(){ // 創造HashMap集合 HashMap<Person,String> map02=new HashMap<>(); // 往集合中添加元素 map02.put(new Person("張三",18),"法外狂徒"); map02.put(new Person("黃老板",18),"英國"); map02.put(new Person("陳奕迅",18),"中國"); map02.put(new Person("張三",18),"法外狂徒"); // 使用迭代器遍歷set集合中的Entry對象 Set<Map.Entry<Person,String>> set=map02.entrySet(); Iterator<Map.Entry<Person,String>> it=set.iterator(); while(it.hasNext()){ Map.Entry<Person,String> entry=it.next(); Person key=entry.getKey(); String value=entry.getValue(); System.out.println(key+"--->"+value); } } }
這里再介紹一下本例中Entry對象遍歷的圖解,再次加深印象:
我們知道HashMap保證key唯一,并且查詢速度快,可是成對元素存放進去是沒有順序的(存和取的順序可能不一致),那我們要如何保證順序呢?
在HashMap下面有個LinkedHashMap(繼承關系),它是鏈表(記錄元素的順序)和哈希表組合的一個數據存儲結構,是個有序的集合
【參考代碼】
package Map; import javax.swing.text.html.HTMLDocument; import java.util.*; public class Test { public static void main(String[] args) { HashMap<String,String> map=new LinkedHashMap<>(); map.put("a","a"); map.put("c","c"); map.put("b","b"); map.put("d","d"); System.out.println(map);//{a=a, c=c, b=b, d=d} } }
輸出結果:(存儲和取出的順序是一樣的)
{a=a, c=c, b=b, d=d}
看到這里,相信各位小伙伴們對Java集合這一章節的知識有了進一步的理解,尤其是一些在之前學習時可能沒有注意到的知識或者原理,沒關系,這次都幫你總結在一起了。最后,感謝看到這里的你!愿你韶華不負,青春無悔!
原文:https://www.cnblogs.com/lwtyyds/p/15167601.html
者 | kakashi8841
責編 | 屠敏
出品 | CSDN 博客
簡述
這篇文章并不是想教會大家如何開發游戲,更不能教大家如何成為技術總監。而是分享我一路做開發的一些經歷或心得體驗。
與編程擦肩而過
2004年,初三升高中時,因為我考上市里最好的高中,因此父母兌現承諾給我買了一臺電腦。那時候電腦配置還是挺好的(我記得是P4的CPU、1G內存、80G硬盤、優派的液晶顯示器,還買了打印機、掃描儀)。
由于我熱愛游戲,因此有了電腦之后就開始陷入于各類游戲中無法自拔。基本每天晚上通宵玩游戲,玩到凌晨5、6點睡1-2個小時,然后去上學。基本高一玩了各種游戲,然后成績自然也是一落千丈。
到了高二的時候,因為我表姐夫是做美術設計的,因此我開始接觸了Photoshop、Coredraw這兩個軟件。我一邊瞎畫著各種東西,一邊想如果能讓這些也動起來多好。因此開始接觸了Flash。開始在網上看看各種Flash的教程,能做一些動畫,能用按鈕控件控制一些流程,也了解到AS2這一門語言,然后在網上找了各種酷炫的AS2代碼加到自己的Flash里面,實現比如鼠標跟隨的特效等等效果。這時候其實我還不會獨立地寫代碼。
由于高中各種的不務正業。從中考數學全市前幾名,到最后高考沒考上重點大學,這其實也是我預料中的。因為本來我就不是一個相信運氣的人。那時候我只想快點脫離高中,可以更自由做自己喜歡做的事情。因此也沒有選擇復讀。
從圖像設計到癡迷編程
入門編程
進入大學之后,我參與了好幾個社團,成為各個社團網絡部的成員。然后接下來的事情就是社團搞活動經常需要一些海報什么的。因此我高中自學的Photoshop和Coredraw就派上用場了。大一基本就是做了一年各種圖像設計的工作。而從高中到大一帶來的各種突如其來的自由,也使得我繼續沉浸在各種游戲中。到了大一的暑假時,校團委突然找網絡部幫忙做網站,而師兄們準備畢業的、準備考研的,都沒啥時間。因此這重任落到了我一個人頭上,我也不知道那么多成員為啥選中了我,也沒想那么多。
我暑假放假前1個月,外加暑假2個月,整個3個月時間開始瘋狂學習HTML、CSS、Javascript、PHP、MySQL,然后3個月時間從學習到開發完成了校團委的任務,制作了一個CMS網站。這三個月時間讓我覺得編程原來這么有趣。
沉迷編程
接下來大二的時候我就陷入了瘋狂的學習和開發之中,最瘋狂的時候一天在圖書館借一本PHP的書,當天借當天看完,晚上再自己把那本書最后的例子實現了,然后第二天再去換書,就這樣我很快把圖書館所有PHP的書都看完了。書中的例子也都能自己寫出來。然后開始接了很多外包,也快速賺到了第一個筆記本的錢。
Java的圖形編程
由于大二做了很多的網站,已經覺得有點厭倦。進入大三,剛好我們有一門Java的課程,我看到Java可以開發圖形界面(Swing/Awt),因此開始轉向學習Java。我大概花了2-3周把學校發的Java書看完了,并且把里面的示例也都自己敲了一遍。基本沒有障礙的完成了Java的學習。但是書本里面關于圖形界面的太少了,只介紹了最基本的一些控件以及做一些簡單的界面。因此我開始自己在網上找各種資料。這時候室友告訴我“Java做圖形不好看的,還不如C++不如C#,等等”。我就偏偏不信了,我依舊進行自己的學習和研究,而他也依舊站在他認為的鄙視鏈頂端鄙視我。就這樣,他學習他的SSH,而我學習著不入流的Swing、Java3D等。
其實我那時候的心理:為啥你覺得他不行他就不行?我得自己實踐過才知道。實踐出真知,我覺得這是做技術一個很重要的特質。很多時候,很多東西大家只是憑經驗人云亦云地傳播,并沒有親自實踐過。而我那時候還有一個心理,哪怕最后我花了很多時間之后做出來的東西確實不好看,那我也不虧,我在這個過程中肯定是能學到東西的。很多時候很多人會比較看重眼前做的事情收益如何,回收周期多長。但是我覺得,很多值得學習的知識都是回收周期很長,而且眼前收益很低的。對于我而言,我更多的是基于興趣去學習。我覺得這就足夠了。
最后的結果就是,我用Java開發出來的東西,他們覺得太好看,根本難以想象是Java開發的。這里有一些大學用Java開發的圖形程序。
1. swing超絢麗系列一—— 俄羅斯方塊(https://kakashi.blog.csdn.net/article/details/7338836)
2. 純Java開發的游戲引擎V0.5–DEMO2 – 物理引擎(https://kakashi.blog.csdn.net/article/details/6397051)
3. 雜七雜八的東西(https://kakashi.blog.csdn.net/article/details/6181486)
就職游戲開發
2011,第一次面試&實習生
2011年初,不知不覺到了大四第二學期。本來我還不想這時候去找工作,因為此時我覺得還有很多事情想做,比如那時候我還在用Java自己做一個3D游戲。
但是大四寒假的時候,有個同學發了一個網頁游戲公司的招聘鏈接給我,說這個公司還不錯,我看了一下招聘要求,感覺自己符合要求,于是就順手投了個簡歷。
其實投簡歷之前,那時候北京GLU有位前輩想讓我畢業后去試試。還有另一家互聯網公司也是讓我畢業后去工作。主要在于我大學期間經常在各個技術QQ群里幫大家解決問題,最開始他們以為我應該工作了幾年,后來知道我還在上學,因此就想我畢業后去他們公司工作。
后來肯定是我投的簡歷的公司讓我去面試了,那天從下午2點面試到下午6點。從HR、主程序、制作人到CTO都面了。CTO面試的時候網絡的BIO、NIO、多線程的安全問題如何解決也都問了。最后CTO說“你簡歷里寫的都是Java做的圖形界面,我們這Java都是做服務器,你接受做服務器嗎“,我怕服務器又是做網站,于是問他”服務器也是做游戲嗎“,他說“肯定是做游戲。我們做的是網游”。因此我就說“可以呀,只要是做游戲就好。”于是CTO接著說“你進我們公司肯定是沒問題的,估計過幾天會給你發Offer,你也不用去面試別的公司了”。
因為我個人也懶得比較,既然CTO也那么說了,我就沒去面試別的公司,在同學那玩了一周,一周后就入職了。
記得那時候CTO還問過我一個問題“你說你這么喜歡寫代碼, 你想寫到幾歲”,我說“30歲吧”。他問我為啥只寫到30歲。我說那時候可能我是技術總監了。就不用寫代碼了。后來我30歲的時候,成為了技術總監。但是我還是喜歡寫代碼。其實興趣是可以一輩子的。我也不知道為啥面試那時候那么回答了。
2011,第一個游戲
剛進入工作比較緊張,總怕哪里做不好。第一周項目組說讓我去改一下服務器列表。我那時候壓根沒有遠程登陸過服務器改東西,主程就給了我IP、用戶名、密碼,以及網頁的位置。于是我趕緊自己搜索了各種資料,在接到任務的半小時內,我第一次用SecureCRT登陸服務器,第一次用VI在服務器上修改文件。
接著幾天就是熟悉項目,我在服務器上自己找到了聊天的協議入口,然后加入了一些GM,給自己的賬號加了趙云、呂布這些牛逼的武將。
幾天后,主程找我,說來的畢業生里面感覺你編程能力不錯,你想去做新項目嗎。我自然想體驗從0開發游戲的過程。于是就滿心雀躍地說“想!”。
新項目開發的時候我充滿了激情,基本上第一周我就完成了背包系統的開發。那時候公司其他游戲的數據存儲都是同步的。主程就說,這個新項目估計用戶交互會比較頻繁和實時,同步存儲怕會卡頓。于是我那一周開發背包的時候,順便被服務器的數據存儲寫了個異步的存儲。(后面該功能還被公司其他那項目組引用),立項一個月后主程就去了騰訊。而我們項目本來是公司的嘗試性項目(公司其他項目都是SLG,而這個項目是RPG),因此也一直得不到重視,一直得不到重視。我們這項目加上我就2個服務器,開發了半年上線了。我對游戲業務非常熟,編程基礎也還行,因此開發速度很虧,一個人開發了70%的系統。那時候很多同學問我,你做那么多,公司給你加工資了沒有。我說沒有,他們說,那沒有你為啥做那么多。
其實有的時候不是你在這個位置才能做這個事情。而是你有能力、主動地去做這個位置的事情,做多了你自然也就是這個位置的人。因為我覺得沒有哪個公司會愿意付出成本讓你冒險。
項目上線的時候,公司也讓我成為了這個項目的服務器主程。那時候我距離實習才半年,一直怕自己做不好,因此一直很努力提升各種能力。由于我開發的功能Bug極少,而且出Bug的時候修改極快,因此項目組的測試負責人說我說最受測試歡迎的程序,直到后來她去了銀漢后遇到她,她還說她在銀漢和測試部說,曾經有個程序員這樣這樣…
寫程序出BUG是常見的事情,想要減少BUG數量,要思維謹慎,業務熟悉,而想要修BUG快速,需要對所寫的東西都爛熟于心,我之前經常對別人說要做到代碼在心中。
如果你寫的代碼你內心都很清晰,那么出問題的時候,很快就能定位到問題。這就像,你的代碼存在于內存中,查找肯定快。如果每次都是要去慢慢看代碼文件,那就像每次都檢索磁盤,肯定會慢一點。我之前好多個性能問題都是在走路和洗澡想到的解決方案,假如心中沒有代碼,又如何能做到呢?
2012年,第二個重量級IP游戲
12年初的時候,公司準備做一個ARPG項目,是星爺授權合作的游戲《西游降魔篇》,那時候公司從我們第一個項目的表現覺得我們項目組還是具備比較好的研發能力的,因此這個項目就給了我們項目組。
一開始公司讓我做這個項目的主程序,我覺得我才畢業1年,我玩游戲很多,自知ARPG項目比我上一個RPG肯定要高。因此最開始希望公司能招一個經驗豐富的主程,我可以過去學習。公司也同意了,主程入職了一直在看天堂2的源碼,也拉著我看。我覺得天堂2的源碼存在很多問題。比如各種在“父類中判斷自己是否是某個子類”等。后來2個月過去了,連登陸都還不能登陸。因此制作人就問我,說你覺得他行不行。我也把我對天堂源碼的判斷說了一下。
后來制作人說,要不還是你來當主程吧。那時候我覺得也行吧。反正本來想跟一個有經驗的人學東西,結果反而他老來問我各種東西,而且開發進度也很慢。于是我就答應了。然后我又重新開始搭框架。第一周花了時間把網絡、副本采用的線程模型搭好了。第二周CTO帶著我做了場景。第二周我們已經可以多客戶端同步移動、怪物有了簡單的AI和普攻了。而且我為了減少和客戶端連調的不便,還自己用Swing寫了一個圖形界面,用于顯示服務器當前地圖中各個角色的位置。(所以說,假如我大學的時候很功利地覺得學習Java的圖形開發性價比不高,那么這時候我肯定沒法自己快速完成這個工具)
這個項目我們花了9個月上線。作為公司第一個ARPG項目,其實也很快了。
項目上線后玩的人挺多,那時候單物理服導量的時候有6000人同時在線。不過這個游戲收費沒有調起來。
2013年,轉戰手游與創業
西游降魔篇調了整整8個月都沒把付費調起來,甚至越來越差。從最開始每月單服的700W變為幾乎沒有。此時已經是2013年中,我建議公司可以使用Unity3D(其實12年底已經說過一次,制作人說次年可以申請看看)開發手游,但是公司的戰略明顯還是在頁游,因為公司靠頁游已經做到借殼上市,不愿意也覺得沒必要去踩手游的坑。公司唯一一個和手游有關的部門其實是把頁游項目通過AIR發布為手游。這樣出來的手游性能堪憂,而且體驗也沒有做針對性優化。
于是在2013年8月,我就辭職自己去創業做手游了。
自己創業肯定面臨的是資金問題,短缺的資金不可能請得了很多牛人,因此我自己又開始做起了Unity3D客戶端。把戰斗等最核心的東西都自己扛下來。
我們2策劃、2程序、1美術從立項到做完Demo花了大概3個月,然后開始拿著手機去演示DEMO,去找投資。
那時候的DEMO是用Unity3D做了一個2D游戲。因為我們唯一的美術人員比較擅長畫畫。Demo可以在這里看到:第一個Unity3D 的Demo(https://kakashi.blog.csdn.net/article/details/29365329)。
我們大概找了4個投資人談,最后確定了投資。
2014-2015,第一次創業
就像上面說的,創業資金的短缺,而且那時候Unity3D人員的稀缺,導致我們想找一個牛逼的人員根本不可能,因此我只能自己扛下戰斗、Shader等核心的工作。那時候白天寫服務器,教新人寫代碼,一行行代碼地邊敲邊講,晚上寫客戶端,最瘋狂的幾個月基本睡公司,凌晨5、6點入睡,早上9點半起來。
那時候我用Unity3D,也只是為了為公司省成本,也沒想什么太多的東西。結果也正是這段經歷,使得我后來成為了另一家百人公司的技術總監。所以我還是挺相信一句話:但行好事,莫問前程!
最后創業肯定還是失敗了,其實還是因為團隊成員的不成熟。因此在2015年中的時候,經過了幾輪調整和測試后,由于測試結果表現不理想,我們就結束了創業。我并沒有對這次創業感到后悔,畢竟那是一次寶貴的經歷。而且整個創業過程也都是很充實,很開心。
2015,西山居和多益網絡
創業結束后我覺得想找個公司積累一下。那時候只面試了珠海西山居和多益網絡,西山居劍網3項目組也希望我過去,但我覺得我就是因為懶,最后選擇了繼續留在廣州的多益網絡。我學習可以很努力,但是對于公司的選擇卻經常是很懶。
2016-2018,第二次創業
在多益做的時間不長,基本過了試用期,以前第一家公司的制作人就找我創業。并不是因為我個人喜歡創業,其實我只想做一個成功的游戲,能被大家認可的游戲。那時候出去創業,更多是因為這位制作人以前對我不錯,也是因為他我才能剛畢業就當上主程,因此這次創業,其實更多的還是報恩。
這次創業的結果無疑還是失敗的。
2018至今,360游戲藝術(豈凡網絡)技術總監
2018年4月,由于以前帶的一位程序的推薦,我便去了360游戲藝術擔任技術總監。這時候剛好30歲,正好和剛畢業實習的時候自己說30歲可能會當上技術總監。實現了自己當初不假思索的一句話。但是回想起來,我這一路并沒有為當技術總監去做任何刻意的學習與準備。只是我一路以來學習的都是自己喜歡的,做的都是自己喜歡的,而且該公司正好需要我目前擁有的能力而已。這也是我最后想和大家分享的,有的人會問怎么才能當主程,怎么才能當總監。我覺得不管當什么職位,重要的就是為公司解決問題,你能解決什么樣的問題,你自然就能勝任什么崗位。而有很多舍本逐末的做法,其實看似近路,最后都是無盡的彎路。我覺得做什么都不重要,重要的應該想想,自己能為游戲這個行業做什么,平常應該怎么提升自我去成為行業真正渴求的人。
當然我從任該崗位以來,一直也覺得自己還是做的不夠好,也還在一直努力提升自己。
今天正好有空,謹以此文,獻給所有想為自己所在行業做點貢獻的小伙伴!希望一起努力,共勉!
聲明:本文已獲 CSDN 博主 kakashi8841 授權,版權歸作者所有。
原文:https://blog.csdn.net/kakashi8841/article/details/100065038
【END】
*請認真填寫需求信息,我們會在24小時內與您取得聯系。