Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the programming skills of Java

2025-03-04 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

Editor to share with you what Java programming skills, I believe that most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to know it!

1. Use HashSet to determine whether the primary key exists

HashSet implements the Set interface, which is supported by a hash table (actually HashMap), but does not guarantee the iterative order of set and allows the use of null elements. The time complexity of HashSet is the same as that of HashMap. If there is no hash conflict, the time complexity is O (1). If there is a hash conflict, the time complexity is not more than O (n). Therefore, in daily coding, you can use HashSet to determine whether the primary key exists.

Example: given a string (not necessarily all letters), return the first recurring character.

/ * * find the first repeating character * / public static Character findFirstRepeatedChar (String string) {/ / check the empty string if (Objects.isNull (string) | | string.isEmpty ()) {return null;} / / find the repeating character char [] charArray = string.toCharArray (); Set charSet = new HashSet (charArray.length) For (char ch: charArray) {if (charSet.contains (ch)) {return ch;} charSet.add (ch);} / / empty return null;} is returned by default

Here, because the add function of Set has a feature-if the added element already exists in the collection, it will return false. You can simplify the code to:

If (! charSet.add (ch)) {return ch;} 2. Access key-value mapping using HashMap

To put it simply, HashMap consists of arrays and linked lists, which are the main body of HashMap, and linked lists exist mainly to resolve hash conflicts. If the location of the located array does not contain a linked list, then the operations such as searching and adding are very fast and only need to be addressed once, and its time complexity is O (1). If the located array contains a linked list, the time complexity of the add operation is O (n)-first traversing the linked list, covering it if it exists, or adding it if it does not exist. For the lookup operation, you still need to traverse the linked list and then compare the lookups one by one through the equals method of the key object. In terms of performance, the fewer linked lists in HashMap, that is, the fewer hash conflicts, the better the performance. Therefore, in daily coding, you can use HashMap to access key-value mapping relationships.

Example: given a list of menu records, each menu record contains the parent menu identity (the parent menu of the root menu is identified as null) to build the entire menu tree.

/ * * menu DO class * / @ Setter@Getter@ToStringpublic static class MenuDO {/ * * menu ID * / private Long id; / * * menu parent ID * / private Long parentId; / * * menu name * / private String name; / * * menu link * / private String url } / * * menu VO class * / @ Setter@Getter@ToStringpublic static class MenuVO {/ * * menu ID * / private Long id; / * * menu name * / private String name; / * * menu link * / private String url; / * * submenu list * / private List childList } / * * build menu tree function * / public static List buildMenuTree (List menuList) {/ / check list is empty if (CollectionUtils.isEmpty (menuList)) {return Collections.emptyList ();} / / process menu int menuSize = menuList.size (); List rootList = new ArrayList (menuSize); Map menuMap = new HashMap (menuSize) For (MenuDO menuDO: menuList) {/ / assignment menu object Long menuId = menuDO.getId (); MenuVO menu = menuMap.get (menuId); if (Objects.isNull (menu)) {menu = new MenuVO (); menu.setChildList (new ArrayList ()); menuMap.put (menuId, menu);} menu.setId (menuDO.getId ()) Menu.setName (menuDO.getName ()); menu.setUrl (menuDO.getUrl ()); / / process Long parentId = menuDO.getParentId () based on parent identity; if (Objects.nonNull (parentId)) {/ / build parent menu object MenuVO parentMenu = menuMap.get (parentId) If (Objects.isNull (parentMenu)) {parentMenu = new MenuVO (); parentMenu.setId (parentId); parentMenu.setChildList (new ArrayList ()); menuMap.put (parentId, parentMenu);} / / add submenu object parentMenu.getChildList () .add (menu) } else {/ / add root menu object rootList.add (menu);}} / / return root menu list return rootList;} 3. Using ThreadLocal to store thread-specific objects

ThreadLocal provides thread-specific objects that can be accessed at any time throughout the thread life cycle, which greatly facilitates the implementation of some logic.

There are two main common uses of ThreadLocal:

Save thread context objects to avoid multi-level parameter passing

Save non-thread-safe objects to avoid multithreaded concurrent calls.

3.1. Save thread context objects to avoid multi-level parameter passing

Here, take the paging parameter setting and use in the source code of the PageHelper plug-in as an example.

Set the paging parameter code:

/ * * paging method class * / public abstract class PageMethod {/ * * Local paging * / protected static final ThreadLocal LOCAL_PAGE = new ThreadLocal (); / * * set paging parameter * / protected static void setLocalPage (Page page) {LOCAL_PAGE.set (page);} / * * get paging parameter * / public static Page getLocalPage () {return LOCAL_PAGE.get () } / * * start paging * / public static Page startPage (int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {Page page = new Page (pageNum, pageSize, count); page.setReasonable (reasonable); page.setPageSizeZero (pageSizeZero); Page oldPage = getLocalPage (); if (oldPage! = null & & oldPage.isOrderByOnly ()) {page.setOrderBy (oldPage.getOrderBy ()) } setLocalPage (page); return page;}}

Use paging parameter codes:

/ * * Virtual auxiliary dialect class * / public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {/ * * get local pagination * / public Page getLocalPage () {return PageHelper.getLocalPage ();} / * * get paging SQL * / @ Override public String getPageSql (MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {String sql = boundSql.getSql (); Page page = getLocalPage () String orderBy = page.getOrderBy (); if (StringUtil.isNotEmpty (orderBy)) {pageKey.update (orderBy); sql = OrderByParser.converToOrderBySql (sql, orderBy);} if (page.isOrderByOnly ()) {return sql;} return getPageSql (sql, page, pageKey);}.}

Use the paging plug-in code:

/ * * query user function * / public PageInfo queryUser (UserQuery userQuery, int pageNum, int pageSize) {PageHelper.startPage (pageNum, pageSize); List userList = userDAO.queryUser (userQuery); PageInfo pageInfo = new PageInfo (userList); return pageInfo;}

If you want to pass the paging parameters to the query statement step by step through the function parameters, it is impossible to achieve it unless you modify the MyBatis-related interface functions.

3.2. Save non-thread-safe objects to avoid multithreaded concurrent calls

When writing the date formatting tool function, the first thing that comes to mind is as follows:

/ * * date mode * / private static final String DATE_PATTERN = "yyyy-MM-dd"; / * * format date function * / public static String formatDate (Date date) {return new SimpleDateFormat (DATE_PATTERN) .format (date);}

Initializing DateFormat for each call results in poor performance. The definition of DateFormat as a constant is written as follows:

/ * date format * / private static final DateFormat DATE_FORMAT = new SimpleDateFormat ("yyyy-MM-dd"); / * format date function * / public static String formatDate (Date date) {return DATE_FORMAT.format (date);}

Because SimpleDateFormat is not thread-safe, when multiple threads call the formatDate function at the same time, the return result is not as expected. If you use ThreadLocal to define thread-specific objects, the optimized code is as follows:

/ * * Local date format * / private static final ThreadLocal LOCAL_DATE_FORMAT = new ThreadLocal () {@ Override protected DateFormat initialValue () {return new SimpleDateFormat ("yyyy-MM-dd");}}; / * format date function * / public static String formatDate (Date date) {return LOCAL_DATE_FORMAT.get () .date (date);}

This is implemented before there is no thread-safe date formatting tool class. After JDK8, it is recommended to use DateTimeFormatter instead of SimpleDateFormat because SimpleDateFormat is thread-safe and DateTimeFormatter is thread-safe. Of course, you can also use thread-safe date formatting functions provided by third parties, such as apache's DateFormatUtils utility class.

Note: ThreadLocal has a certain risk of memory leakage, so try to call the remove function to clean up the data before the end of the business code.

4. Using Pair to realize the return of paired results

In the C _ std::pair + language, Pair (pair) is a container that combines two data types into one data type, such as std::pair.

Pair has two main uses:

Put key and value together in pairs, which are mainly used to return name-value pairs in Map, such as the Entry class in Map.

When a function needs to return two results, you can use Pair to avoid defining too many data model classes.

The first use is relatively common, and the second use is mainly described here.

4.1. Define the model class to realize the return of paired results

Function implementation code:

/ * Setter@Getter@ToString@AllArgsConstructorpublic static class PointAndDistance {/ * * Point * / private Point point; / * distance * / private Double distance;} / * * get the closest point and distance * / public static PointAndDistance getNearestPointAndDistance (Point point, Point [] distance) {/ / check point array is empty if (ArrayUtils.isEmpty (points)) {return null } / / get the nearest point and distance Point nearestPoint = points [0]; double nearestDistance = getDistance (point, points [0]); for (int I = 1; I

< points.length; i++) { double distance = getDistance(point, point[i]); if (distance < nearestDistance) { nearestDistance = distance; nearestPoint = point[i]; } } // 返回最近点和距离 return new PointAndDistance(nearestPoint, nearestDistance);} 函数使用案例: Point point = ...;Point[] points = ...;PointAndDistance pointAndDistance = getNearestPointAndDistance(point, points);if (Objects.nonNull(pointAndDistance)) { Point point = pointAndDistance.getPoint(); Double distance = pointAndDistance.getDistance(); ...}4.2.使用Pair类实现成对结果的返回 在JDK中,没有提供原生的Pair数据结构,也可以使用Map::Entry代替。不过,Apache的commons-lang3包中的Pair类更为好用,下面便以Pair类进行举例说明。 函数实现代码: /** 获取最近点和距离 */public static Pair getNearestPointAndDistance(Point point, Point[] points) { // 检查点数组为空 if (ArrayUtils.isEmpty(points)) { return null; } // 获取最近点和距离 Point nearestPoint = points[0]; double nearestDistance = getDistance(point, points[0]); for (int i = 1; i < points.length; i++) { double distance = getDistance(point, point[i]); if (distance < nearestDistance) { nearestDistance = distance; nearestPoint = point[i]; } } // 返回最近点和距离 return Pair.of(nearestPoint, nearestDistance);} 函数使用案例: Point point = ...;Point[] points = ...;Pair pair = getNearestPointAndDistance(point, points);if (Objects.nonNull(pair)) { Point point = pair.getLeft(); Double distance = pair.getRight(); ...}5.定义Enum类实现取值和描述 在C++、Java等计算机编程语言中,枚举类型(Enum)是一种特殊数据类型,能够为一个变量定义一组预定义的常量。在使用枚举类型的时候,枚举类型变量取值必须为其预定义的取值之一。 5.1.用class关键字实现的枚举类型 在JDK5之前,Java语言不支持枚举类型,只能用类(class)来模拟实现枚举类型。 /** 订单状态枚举 */public final class OrderStatus { /** 属性相关 */ /** 状态取值 */ private final int value; /** 状态描述 */ private final String description; /** 常量相关 */ /** 已创建(1) */ public static final OrderStatus CREATED = new OrderStatus(1, "已创建"); /** 进行中(2) */ public static final OrderStatus PROCESSING = new OrderStatus(2, "进行中"); /** 已完成(3) */ public static final OrderStatus FINISHED = new OrderStatus(3, "已完成"); /** 构造函数 */ private OrderStatus(int value, String description) { this.value = value; this.description = description; } /** 获取状态取值 */ public int getValue() { return value; } /** 获取状态描述 */ public String getDescription() { return description; }}5.2.用enum关键字实现的枚举类型 JDK5提供了一种新的类型--Java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常量使用,这是一种非常有用的功能。 /** 订单状态枚举 */public enum OrderStatus { /** 常量相关 */ /** 已创建(1) */ CREATED(1, "已创建"), /** 进行中(2) */ PROCESSING(2, "进行中"), /** 已完成(3) */ FINISHED(3, "已完成"); /** 属性相关 */ /** 状态取值 */ private final int value; /** 状态描述 */ private final String description; /** 构造函数 */ private OrderStatus(int value, String description) { this.value = value; this.description = description; } /** 获取状态取值 */ public int getValue() { return value; } /** 获取状态描述 */ public String getDescription() { return description; }} 其实,Enum类型就是一个语法糖,编译器帮我们做了语法的解析和编译。通过反编译,可以看到Java枚举编译后实际上是生成了一个类,该类继承了 java.lang.Enum,并添加了values()、valueOf()等枚举类型通用方法。 6.定义Holder类实现参数的输出 在很多语言中,函数的参数都有输入(in)、输出(out)和输入输出(inout)之分。在C/C++语言中,可以用对象的引用(&)来实现函数参数的输出(out)和输入输出(inout)。但在Java语言中,虽然没有提供对象引用类似的功能,但是可以通过修改参数的字段值来实现函数参数的输出(out)和输入输出(inout)。这里,我们叫这种输出参数对应的数据结构为Holder(支撑)类。 Holder类实现代码: /** 长整型支撑类 */@Getter@Setter@ToStringpublic class LongHolder { /** 长整型取值 */ private long value; /** 构造函数 */ public LongHolder() {} /** 构造函数 */ public LongHolder(long value) { this.value = value; }} Holder类使用案例: /** 静态常量 *//** 页面数量 */private static final int PAGE_COUNT = 100;/** 最大数量 */private static final int MAX_COUNT = 1000;/** 处理过期订单 */public void handleExpiredOrder() { LongHolder minIdHolder = new LongHolder(0L); for (int pageIndex = 0; pageIndex < PAGE_COUNT; pageIndex++) { if (!handleExpiredOrder(pageIndex, minIdHolder)) { break; } }}/** 处理过期订单 */private boolean handleExpiredOrder(int pageIndex, LongHolder minIdHolder) { // 获取最小标识 Long minId = minIdHolder.getValue(); // 查询过期订单(按id从小到大排序) List orderList = orderDAO.queryExpired(minId, MAX_COUNT); if (CollectionUtils.isEmpty(taskTagList)) { return false; } // 设置最小标识 int orderSize = orderList.size(); minId = orderList.get(orderSize - 1).getId(); minIdHolder.setValue(minId); // 依次处理订单 for (OrderDO order : orderList) { ... } // 判断还有订单 return orderSize >

= PAGE_SIZE;}

In fact, you can implement a generic support class that is suitable for more data types.

7. Define Union class to realize the coexistence of data bodies.

In struct + language, union, also known as common body, is a data structure similar to struct. Union, like struct, can contain many data types and variables. The differences between the two are as follows:

All variables in the structure (struct) "coexist", all variables take effect, and each variable occupies different memory space.

In union, variables are "mutually exclusive", and only one variable takes effect, and all variables occupy the same block of memory space.

When multiple data needs shared memory or only takes one of them at a time, union can be used.

In the Java language, there is no concept of union and struct, only the concept of class. As we all know, struct can be implemented by class. In fact, union can also be implemented with class. However, this class does not have the function of "multiple data needs to be shared in memory", only "multiple data only one at a time" function.

Here, take the customer message of Wechat agreement as an example. According to my years of experience in interface protocol encapsulation, there are mainly two ways to implement it.

7.1. Using function to implement Union

The Union class implementation:

/ * * customer message class * / @ ToStringpublic class CustomerMessage {/ * attribute related * / / * * message type * / private String msgType; / * * target user * / private String toUser; / * * Commons related * / / * * News content * / private News news . / * constant dependent * / / * * News news * / public static final String MSG_TYPE_NEWS = "news"; / * * Constructor * / public CustomerMessage () {} / * * Constructor * / public CustomerMessage (String toUser) {this.toUser = toUser } / * * Constructor * / public CustomerMessage (String toUser, News news) {this.toUser = toUser; this.msgType = MSG_TYPE_NEWS; this.news = news;} / * * clear message content * / private void removeMsgContent () {/ / check message type if (Objects.isNull (msgType)) {return } / / clear message content if (MSG_TYPE_NEWS.equals (msgType)) {news = null;} else if (...) {...} msgType = null } / * * check message type * / private void checkMsgType (String msgType) {/ / check message type if (Objects.isNull (msgType)) {throw new IllegalArgumentException ("message type is empty") } / / compare message type if (! Objects.equals (msgType, this.msgType)) {throw new IllegalArgumentException ("message type mismatch");}} / * * set message type function * / public void setMsgType (String msgType) {/ / clear message content removeMsgContent () / / check the message type if (Objects.isNull (msgType)) {throw new IllegalArgumentException ("message type is empty");} / / assign message content this.msgType = msgType; if (MSG_TYPE_NEWS.equals (msgType)) {news = new News () } else if (...) {...} else {throw new IllegalArgumentException ("message type is not supported");}} / * * get message type * / public String getMsgType () {/ / check message type if (Objects.isNull (msgType)) {throw new IllegalArgumentException ("invalid message type") } / / return message type return this.msgType;} / * * set news * / public void setNews (News news) {/ / clear message content removeMsgContent (); / / assign message content this.msgType = MSG_TYPE_NEWS; this.news = news } / * * get news * / public News getNews () {/ / check message type checkMsgType (MSG_TYPE_NEWS); / / return message content return this.news;}.}

The Union class uses:

String accessToken =...; String toUser =...; List articleList =...; News news = new News (articleList); CustomerMessage customerMessage = new CustomerMessage (toUser, news); wechatApi.sendCustomerMessage (accessToken, customerMessage)

Main advantages and disadvantages:

Pros: closer to union, a combination of Cstroke + languages.

Disadvantages: the implementation logic is more complex, and there are many parameter type verification.

7.2. Using inheritance to implement Union

The Union class implementation:

/ * * customer message class * / @ Getter@Setter@ToStringpublic abstract class CustomerMessage {/ * attribute related * / / * * message type * / private String msgType; / * * target user * / private String toUser; / * * constant related * / / * * News message * / public static final String MSG_TYPE_NEWS = "news" / * * Constructor * / public CustomerMessage (String msgType) {this.msgType = msgType;} / * * Constructor * / public CustomerMessage (String msgType, String toUser) {this.msgType = msgType; this.toUser = toUser }} / * * News customer message class * / @ Getter@Setter@ToString (callSuper = true) public class NewsCustomerMessage extends CustomerMessage {/ * * attribute related * / / * * News content * / private News news; / * * constructor * / public NewsCustomerMessage () {super (MSG_TYPE_NEWS) } / * * constructor * / public NewsCustomerMessage (String toUser, News news) {super (MSG_TYPE_NEWS, toUser); this.news = news;}}

The Union class uses:

String accessToken =...; String toUser =...; List articleList =...; News news = new News (articleList); CustomerMessage customerMessage = new NewsCustomerMessage (toUser, news); wechatApi.sendCustomerMessage (accessToken, customerMessage)

Main advantages and disadvantages:

Advantages: use virtual base classes and subclasses to split, and the concept of each subclass object is clear

Cons: it is quite different from the union of the CumberCraft + language, but it is roughly the same in function.

The federation does not include the current data type of the federation in the CAccord + language. However, in the Java consortium implemented above, the data type corresponding to the union is already included. So, strictly speaking, the Java consortium is not a real union, but a class with the function of "multiple data one at a time".

8. Differences in using generic masking types

In the C++ language, there is a good template function to write general versions with parameterized types, so that the compiler can automatically generate specific versions for different types. In the Java language, there is a similar feature called generic. When writing classes and methods, specific types are generally used, and generics can be used to parameterize types so that more generic code can be written.

Many people think that the concepts of C++ template (template) and Java generics (generic) are equivalent, but the implementation mechanisms are completely different. C++ templates are a set of macro instructions, and the compiler creates a copy of the template code for each type; the implementation of Java generics is based on the concept of "type erasure" and is essentially a syntactic sugar for type restriction.

8.1. Generic class

Taking a support class as an example, define a generic support class for generics:

/ * * General support class * / @ Getter@Setter@ToStringpublic class GenericHolder {/ * General value * / private T value; / * * Constructor * / public GenericHolder () {} / * * Constructor * / public GenericHolder (T value) {this.value = value;}} 8.2. Generic interface

Define the data provider interface for generics:

/ * * data provider interface * / public interface DataProvider {/ * * get data function * / public T getData ();} 8.3. Generic method

Define a shallow copy function for generics:

/ * * shallow copy function * / public static T shallowCopy (Object source, Class clazz) throws BeansException {/ / determine source object if (Objects.isNull (source)) {return null;} / / New target object T target; try {target = clazz.newInstance ();} catch (Exception e) {throw new BeansException ("New Class instance exception", e) } / / copy object properties BeanUtils.copyProperties (source, target); / / return target object return target;} 8.4. Generic wildcard character

Generic wildcards generally use "?" Instead of specific type arguments, you can set "?" As the parent of all types. When the specific type is uncertain, you can use the generic wildcard "?" When you do not need to use the specific features of the type, but only use the functions in the Object class, you can use the generic wildcard "?" .

/ * * print value function * / public static void printValue (GenericHolder holder) {System.out.println (holder.getValue ());} / * * main function * / public static void main (String [] args) {printValue (new GenericHolder (12345)); printValue (new GenericHolder ("abcde"));}

In the Java specification, the generic wildcard "?" is not recommended. The above function can be changed to:

/ * * print value function * / public static void printValue (GenericHolder holder) {System.out.println (holder.getValue ());} 8.5. Upper and lower bounds of generics

When using generics, we can also impose upper and lower bounds on the passed generic type arguments, for example, type arguments are only allowed to pass in a certain type of parent class or a certain type of subclass. The declaration of the upper and lower bounds of generics must be put together with the declaration of generics.

Upper wildcard (extends):

The upper wildcard is "extends" and can accept its specified type or its subclass as a generic parameter. It also has a special form in which it can be specified not only as a subclass of the specified type, but also to implement certain interfaces. For example: List

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report