java基础知识
Posted little-horse
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java基础知识相关的知识,希望对你有一定的参考价值。
1.list删除元素隐藏问题
Apple apple1 = new Apple(1,"苹果1",new BigDecimal("3.25"),10);
Apple apple12 = new Apple(1,"苹果2",new BigDecimal("1.35"),20);
Apple apple2 = new Apple(2,"香蕉",new BigDecimal("2.89"),30);
Apple apple3 = new Apple(3,"荔枝",new BigDecimal("9.99"),40);
ArrayList<Apple> list = new ArrayList<>();
list.add(apple1);
list.add(apple12);
list.add(apple2);
list.add(apple3);
//方式1:使用原始for
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getId().equals(1)){
list.remove(i); // 会发现有个id为1的元素删除不掉。
}
}
list.forEach(item-> System.out.println(item)); //这个使用java8的lambda表达式
//这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
//方式2:使用增强的for(foreach循环)
for (Apple apple : list) {
if (apple.getId().equals(1)){
list.remove(apple); //报了著名的java.util.ConcurrentModificationException并发修改异常
}
}
list.forEach(item-> System.out.println(item));
//这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。
//方式3:使用迭代器遍历
Iterator<Apple> iterator = list.iterator();
while(iterator.hasNext()){
if (iterator.next().getId().equals(1)){
iterator.remove(); //这里的删除不能选择list,还是只能使用迭代器来删除
}
}
//这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。
总结:
(1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。
(2)循环删除list中多个元素的,应该使用迭代器iterator方式。
2.lambda表达式 list->map
//list->map
Map<Integer, Apple> map = list.stream().collect(Collectors.toMap(Apple::getId, a -> a, (k1, k2) -> k1));
//第一种打印方法:
map.forEach((k,v)-> System.out.println(k + "->"+ v));
System.out.println();
//第二种:
Set<Map.Entry<Integer, Apple>> entrySet = map.entrySet();
for(Map.Entry entry: entrySet){
System.out.println(entry.getKey()+"->"+entry.getValue());
}
System.out.println();
//第三种:
Collection<Apple> collection = map.values();
for (Apple value : collection) {
System.out.println(value);
}
//去重
ArrayList<Apple> newList = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(()
-> new TreeSet<>(Comparator.comparingLong(Apple::getId))), ArrayList::new));
newList.forEach(item-> System.out.println(item));
3.spring注解
- @PostConstruct
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的init()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
- @PreDestroy
被@PreDestroy修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。
- @Bean
告诉Spring,一个带有@Bean注解的方法将返回一个对象,该对象应该被注册到spring容器中。
- @Configuration
能生产出让Spring IoC容器管理的Bean实例的工厂。
- @ConditionalOnMissingBean
该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean;可以给该注解传入参数例如@ConditionOnMissingBean(name = "example"),这个表示如果name为“example”的bean存在,这该注解修饰的代码块不执行。
- @ConditionalOnProperty:存在多个数据源的时候,让其中某一个数据源(配置类)生效。
spring boot中有个注解@ConditionalOnProperty,这个注解能够控制某个configuration是否生效。
具体操作是通过其两个属性name以及havingValue来实现的,其中name用来从application.properties中读取某个属性值,如果该值为空,则返回false;
如果值不为空,则将该值与havingValue指定的值进行比较,如果一样则返回true;否则返回false。如果返回值为false,则该configuration不生效;为true则生效
@Configuration
//如果synchronize在配置文件中并且值为true
@ConditionalOnProperty(name = "synchronize", havingValue = "true")
public class SecondDatasourceConfig {
@Bean(name = "SecondDataSource")
@Qualifier("SecondDataSource")
@ConfigurationProperties(prefix = "spring.second.datasource")
public DataSource jwcDataSource() {
return DataSourceBuilder.create().build();
}
}
4.线程池
ThreadPoolExecutor中三个线程池关闭的方法:shutdown()、shutdownNow()、awaitTermination()
shutdown方法:将线程池状态置为SHUTDOWN。平滑的关闭ExecutorService,当此方法被调用时,ExecutorService停止接收新的任务并且等待已经提交的任务(包含提交正在执行和提交未执行)执行完成。当所有提交任务执行完毕,线程池即被关闭。
awaitTermination方法:接收timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。
shutdownNow方法:将线程池状态置为STOP。跟shutdown()一样,先停止接收外部提交的任务,忽略队列里等待的任务,尝试将正在跑的任务interrupt中断,返回未执行的任务列表。
具体实例:
普通任务处理类:
public class Task implements Callable{
@Override
public Object call() throws Exception {
System.out.println("普通任务");
return null;
}
}
长时间任务处理类:
public class LongTask implements Callable{
@Override
public Object call() throws Exception {
System.out.println("长时间任务");
TimeUnit.SECONDS.sleep(5);
return null;
}
}
测试类:
public class TestShutDown {
public static void main(String[] args) throws InterruptedException{
ScheduledExecutorService service = Executors.newScheduledThreadPool(4);
service.submit(new Task());
service.submit(new Task());
service.submit(new LongTask());
service.submit(new Task());
service.shutdown();
while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
System.out.println("线程池没有关闭");
}
System.out.println("线程池已经关闭");
}
}
5.对象池
6.日期
SimpleDateFormat :线程不安全,单线程下没问题。
public class SimpleDateFormatTest {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) throws ParseException {
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException {
return sdf.parse(strDate);
}
public static void main(String[] args) throws InterruptedException, ParseException {
System.out.println(sdf.format(new Date()));
}
}
测试类:
public static void main(String[] args) throws InterruptedException, ParseException {
ExecutorService service = Executors.newFixedThreadPool(100);
for (int i = 0; i < 20; i++) {
service.execute(() -> {
for (int j = 0; j < 10; j++) {
try {
System.out.println(parse("2018-01-02 09:45:59"));
} catch (ParseException e) {
e.printStackTrace();
}
}
});
}
// 等待上述的线程执行完
service.shutdown();
service.awaitTermination(1, TimeUnit.DAYS);
}
解决方法:
1.只在需要的时候创建新实例,不用static修饰。
仅在需要用到的地方创建一个新的实例,就没有线程安全问题,不过也加重了创建对象的负担,会频繁地创建和销毁对象,效率较低。
public static String formatDate(Date date) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public static Date parse(String strDate) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.parse(strDate);
}
2.synchronized大法好
简单粗暴,synchronized往上一套也可以解决线程安全问题,缺点自然就是并发量大的时候会对性能有影响,线程阻塞。
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date) throws ParseException {
synchronized(sdf){
return sdf.format(date);
}
}
public static Date parse(String strDate) throws ParseException {
synchronized(sdf){
return sdf.parse(strDate);
}
}
3.ThreadLocal
ThreadLocal可以确保每个线程都可以得到单独的一个SimpleDateFormat的对象,那么自然也就不存在竞争问题了。
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
4.基于JDK1.8的DateTimeFormatter__阿里巴巴开发手册
public class SimpleDateFormatTest {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate2(LocalDateTime date) {
return formatter.format(date);
}
public static LocalDateTime parse2(String dateNow) {
return LocalDateTime.parse(dateNow, formatter);
}
public static void main(String[] args) throws InterruptedException, ParseException {
ExecutorService service = Executors.newFixedThreadPool(100);
// 20个线程
for (int i = 0; i < 20; i++) {
service.execute(() -> {
for (int j = 0; j < 10; j++) {
try {
System.out.println(parse2(formatDate2(LocalDateTime.now())));
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// 等待上述的线程执行完
service.shutdown();
service.awaitTermination(1, TimeUnit.DAYS);
}
}
7.java并发编程之CountDownLatch
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
CountDownLatch类只提供了一个构造器:
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1
简单实例:
public class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
线程Thread-0正在执行
线程Thread-1正在执行
等待2个子线程执行完毕...
线程Thread-0执行完毕
线程Thread-1执行完毕
2个子线程已经执行完毕
继续执行主线程
8.java编程之文件上传
// 获取用户头像并保存到本地
try {
String imagePath = PropertyUtil.getProperty("HEAD_IMAGE_PATH") + user + "/";
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
// 5M
upload.setFileSizeMax(1024*1024*5);
InputStream in = new URL(headimgurl).openStream();
if(in != null && in.available()>0){
String imagefullPath = PropertyUtil.getProperty("IMAGE_DIR_ROOT") + imagePath;
boolean success = QRImageUtils.saveImage(in, imagefullPath, imageName);
if(!success){
logger.info("图片上传失败");
}
}
} catch (Exception e) {
e.printStackTrace();
logger.error(e.toString());
logger.error("图片上传失败");
}
public static boolean saveImaeg(InputStream in, String destDirName, String imgName){
try{
if(!createDir(destDirName)){
return false;
}
File file = new File(destDirName,imgName);
FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int read = 0;
while(read = in.read(b) != -1){
fos.write(b,0,read);
}
fos.flush();
fos.close();
in.close();
} catch(){
e.printStackTrace();
logger.error("图片上传失败");
return false;
}
return true;
}
public static boolean createDir(String destDirName){
File file = new File(destDirName);
if(file.exists()){
return true;
}
if(!destDirName.endWith(File.separator)){
destDirName = destDirName + File.separator;
}
//创建目录
if(file.mkdir()){
return true;
} else {
logger.error("创建目录" + destDirName + "失败!");
return false;
}
}
9.java编程之阻塞队列BlockingQueue
BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的。下面的源码以ArrayBlockingQueue为例。
BlockingQueue内部有一个ReentrantLock,其生成了两个Condition,在ArrayBlockingQueue的属性声明中可以看见:
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
...
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
-----------------
ArrayBlockingQueue.put(E e)源码如下
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await(); // 如果队列已满,则等待
insert(e);
} finally {
lock.unlock();
}
}
private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal(); // 有新的元素被插入,通知等待中的取走元素线程
}
ArrayBlockingQueue.take()源码如下:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await(); // 如果队列为空,则等待
return extract();
} finally {
lock.unlock();
}
}
private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]);
items[takeIndex] = null;
takeIndex = inc(takeIndex);
--count;
notFull.signal(); // 有新的元素被取走,通知等待中的插入元素线程
return x;
}
可以看见,put(E)与take()是同步的,在put操作中,当队列满了,会阻塞put操作,直到队列中有空闲的位置。而在take操作中,当队列为空时,会阻塞take操作,直到队列中有新的元素。
而这里使用两个Condition,则可以避免调用signal()时,会唤醒相同的put或take操作。
10.lambda表达式之线程:
public static ExecutorService executorService = Executors.newCachedThreadPool();
try {
executorService.execute(new Runnable() {
public void run() {
System.out.println("run Runnable task");
}
});
Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("submit Runnable task");
}
});
System.out.println(future.get());
Future future2 = executorService.submit(new Callable(){
public String call() throws Exception {
System.out.println("submit Callable task");
return "Callable Result";
}
});
System.out.println("future.get() = " + future2.get() + " type: " + future2.getClass());
/**
*lambda表达式方式:
*/
executorService.execute(() -> System.out.println("run Runnable task"));
Future future3 = executorService.submit(() -> System.out.println("run Runnable task"));
System.out.println(future3.get());
Future future4 = executorService.submit(() -> {
System.out.println("submit Callable task");
return "Callable Result";
});
System.out.println("future.get() = " + future4.get() + ", type: " + future4.getClass());
/**
* 比较器
*/
Comparator<String> comparator1 = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return 0;
}
};
Comparator<String> comparator = Comparator.comparingInt(String::length);
int compare = comparator.compare("abc", "ab");
System.out.println(compare);
List<String> words = Arrays.asList("if", "you", "have", "something", "to", "say", "then", "say!",
"if", "you", "have", "nothing", "to", "say", "go", "home!");
Collections.sort(words, Comparator.comparingInt(String::length));
System.out.println(words);
11.java IO流
package com.gw.test;
import org.junit.Test;
import java.io.*;
public class Test2321 {
/**
* 字节输入流
* @throws IOException
*/
@Test
public void test() throws IOException {
FileInputStream fileInputStream = null;
fileInputStream = new FileInputStream("a.txt");
byte[] buff = new byte[1024];
int len;
while ((len = fileInputStream.read()) != -1) {
System.out.println(new String(buff, 0, len));
}
fileInputStream.close();
}
/**
* 字节输入流+缓冲区
* @throws IOException
*/
@Test
public void test2() throws IOException {
FileInputStream fileInputStream = null;
fileInputStream = new FileInputStream("a.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
byte[] buff = new byte[1024];
int len;
while ((len = bufferedInputStream.read()) != -1) {
System.out.println(new String(buff, 0, len));
}
bufferedInputStream.close();
fileInputStream.close();
}
/**
* 字节输出流
* @throws IOException
*/
@Test
public void test3() throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("b.txt");
fileOutputStream.write("i love you".getBytes());
fileOutputStream.close();
}
/**
* 字节输出流 + 缓冲区
* @throws IOException
*/
@Test
public void test4() throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("b.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write("i love you".getBytes());
fileOutputStream.close();
bufferedOutputStream.close();
}
/**
* 字符输入流InputStreamReader
* @throws IOException
*/
@Test
public void test5() throws IOException {
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("c.txt"));
byte[] buff = new byte[1024];
int len=0;
while((len = inputStreamReader.read()) != -1){
System.out.println(new String(buff,0,len));
}
inputStreamReader.close();
}
/**
* 字符输入流FileReader
* @throws IOException
*/
@Test
public void test6() throws IOException {
FileReader fileReader = new FileReader("d.txt");
byte[] buff = new byte[1024];
int len=0;
while((len = fileReader.read()) != -1){
System.out.println(new String(buff,0,len));
}
fileReader.close();
}
/**
* 字符输入流FileReader+缓冲区
* @throws IOException
*/
@Test
public void test7() throws IOException {
FileReader fileReader = new FileReader("d.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while((line = bufferedReader.readLine()) != null){
System.out.println(line);
}
fileReader.close();
bufferedReader.close();
}
/**
* 字符输出流OutputStreamWriter
* @throws IOException
*/
@Test
public void test8() throws IOException {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("c.txt"));
outputStreamWriter.write("i love you ");
outputStreamWriter.close();
}
/**
* 字符输出流FileWriter
* @throws IOException
*/
@Test
public void test9() throws IOException {
FileWriter fileWriter = new FileWriter("d.txt");
fileWriter.write("i love you");
fileWriter.close();
}
/**
* 字符输出流FileReader+缓冲区
* @throws IOException
*/
@Test
public void test10() throws IOException {
FileWriter fileWriter = new FileWriter("d.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("i love you");
fileWriter.close();
bufferedWriter.close();
}
}
11.单例模式
/**
* 为什么要使用单例模式:
* 1.对于频繁使用的对象,可以省略创建对象所耗费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
* 2.由于new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。
*/
/**
* 懒汉式+双重检查加锁版本
*/
static class Singleton1{
private volatile static Singleton1 instance = null;
private Singleton1(){}
public static Singleton1 getInstance() {
if (instance == null){
synchronized (Singleton1.class){
if (instance == null){
instance = new Singleton1();
}
}
}
return instance;
}
}
/**
* 静态内部类,线程安全
*/
static class Singleton2{
private static class SingletonHolder{
private static final Singleton2 INSTANCE = new Singleton2();
}
private Singleton2(){}
public static Singleton2 getInstance(){
return SingletonHolder.INSTANCE;
}
}
12.如何避免servlet线程安全问题
尽可能不创建成员变量,使用局部变量代替成员变量。因为多个线程同时访问的是同一个成员变量,会共享该实例变量,而在访问局部变量时,每个线程都会有自己的变量,不会共享。如下图:
bankId为成员变量,当多个线程并发访问时,会出现线程安全问题;
而bankName为局部变量,是线程安全的。
所以我们解决bankId线程安全问题,可以把bankId设置为局部变量,如下图所示
class TestServlet extends HttpServlet {// 3.implements SingleThreadModel 该方法已经过时
private String bankId;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//bankId = req.getParameter("bankId");
// 1.利用局部变量替换实例变量
String bankId = req.getParameter("bankId");
// 2.同步
synchronized (bankId){
bankId = req.getParameter("bankId");
}
String bankName = req.getParameter("backName");
}
}
13.检查数组是否包含某个值的方法
// 1.使用list
public static boolean useList(String[] arr, String targetValue) {
return Arrays.asList(arr).contains(targetValue);}
// 2.使用set
public static boolean useSet(String[] arr, String targetValue) {
Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);}
//3. 使用循环判断
public static boolean useLoop(String[] arr, String targetValue) {
for(String s: arr){
if(s.equals(targetValue))
return true;
}
return false;
// 4.使用Arrays.binarySearch()!注意,数组必须是有序的
public static boolean useArraysBinarySearch(String[] arr, String targetValue) {
int a = Arrays.binarySearch(arr, targetValue);
if(a > 0)
return true;
else
return false;}
// 5.使用ArrayUtils
import org.apache.commons.lang3.ArrayUtils;
public static boolean useArrayUtils(String[] arr, String targetValue) {
return ArrayUtils.contains(arr,targetValue);}
结论:使用一个简单的循环方法比使用任何集合都更加高效。许多开发人员为了方便,都使用第一种方法,但是他的效率也相对较低。因为将数组压入Collection类型中,首先要将数组元素遍历一遍,然后再使用集合类做其他操作。
14.项目中pom.xml使得某个环境生效
<activeByDefault>true<activeByDefault>
我们可以在profile中的activation元素中指定激活条件,当没有指定条件,然后指定activeByDefault为true的时候就表示当没有指定其他profile为激活状态时,该profile就默认会被激活。所以当我们调用mvn package的时候上面的profileTest1将会被激活,但是当我们使用mvn package –P profileTest2的时候将激活profileTest2,而这个时候profileTest1将不会被激活。
<profiles>
<profile>
<id>profileTest1</id>
<properties>
<hello>world</hello>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>profileTest2</id>
<properties>
<hello>andy</hello>
</properties>
</profile>
</profiles>
// 项目中的例子,这里主要是对于不同的环境选择不同资源文件(配置)
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<resources>
<resource>
<directory>resource/dev</directory>
</resource>
</resources>
</build>
</profile>
15.Arrays.asList() 注意的点:
String[] str = {"1","2","3"};
String str2 = "1";
//List<String> list = Arrays.asList(str);
//list.add("4"); //java.lang.UnsupportedOperationException
//list.set(2,"4");
//System.out.println(list.toString());
List<String> list2 = Collections.singletonList(str2);
//list2.add("4"); //java.lang.UnsupportedOperationException
System.out.println(list2.toString());
// 编码常用:这样就可以调用add方法
ArrayList arrayList = new ArrayList(Arrays.asList("1","2","3"));
arrayList.add("4");
System.out.println(arrayList.toString());
16.list去重的几种方式
/**
* List去重的问题List去重的问题
*/
public class RemoveRepeat {
public static void main(String[] args) {
List<User> userList = new ArrayList<User>();
userList.add(new User("小黄",10));
userList.add(new User("小红",23));
userList.add(new User("小黄",78));
userList.add(new User("小黄",10));
removeDuplicate1(userList);
removeDuplicate2(userList);
removeDuplicate3(userList);
removeDuplicate4(userList);
removeDuplicate5(userList);
removeDuplicate6(userList);
removeDuplicate7(userList);
removeDuplicate8(userList);
}
public static <E> void removeDuplicate1(List<E> list){
ArrayList<E> listNew = new ArrayList<>();
for (E item : list){
if (!listNew.contains(item)){
listNew.add(item);
}
}
System.out.println(listNew);
}
public static <E> void removeDuplicate2(List<E> list){
ArrayList<E> listNew = new ArrayList<>();
HashSet<E> set = new HashSet<>();
for (E item : list){
if (set.add(item)){
listNew.add(item);
}
}
System.out.println(listNew);
}
// 无序
public static <E> void removeDuplicate3(List<E> list){
ArrayList<E> listNew = new ArrayList<>();
HashSet<E> set = new HashSet<>();
set.addAll(list);
listNew.addAll(set);
System.out.println(listNew);
}
// 无序
public static <E> void removeDuplicate4(List<E> list){
ArrayList<E> listNew = new ArrayList<>(new HashSet<>(list));
System.out.println(listNew);
}
// 无序
public static <E> void removeDuplicate5(List<E> list){
ArrayList<E> listNew = new ArrayList<E>(new LinkedHashSet<E>(list));
System.out.println(listNew);
}
// java8方式
public static <E> void removeDuplicate6(List<E> list){
List<E> listNew = list.stream().distinct().collect(Collectors.toList());
System.out.println(listNew);
}
// java8 去重变形1:
public static void removeDuplicate7(List<User> list){
List<User> listNew = list.stream().collect(collectingAndThen(
toCollection(() -> new TreeSet<User>(comparing(o->o.getName()))),ArrayList::new));
System.out.println(listNew);
}
// java8 去重变形2:
public static void removeDuplicate8(List<User> list){
List<User> listNew = list.stream().collect(collectingAndThen(
toCollection(() -> new TreeSet<User>(comparing(o->o.getName()+";"+o.getAge()))),ArrayList::new));
System.out.println(listNew);
}
// 注意,以上的方法在list元素为基本数据类型及String类型是可以的,如果是对象,那么必须要重写对象的hashcode和equals方法
@Data
static class User{
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
}
17.泛型的使用:
在 Java 泛型中存在通配符的概念:
- <? extends T>:上界通配符(Upper Bounds Wildcards)
- <? super T>:下界通配符(Lower Bounds Wildcards)
18.水平拆分和垂直拆分(不单单指的是对数据库的拆分):
1.水平拆分
水平拆分是指由于单一节点无法满足需求,需要扩展为多个节点,多个节点具有一致的功能,组成一个服务池,一个节点服务一部分请求量,所有节点共同处理大规模高并发的请求量。
2.垂直拆分
垂直拆分指按照功能进行拆分,秉着“专业的人干专业的事”的原则,把一个复杂的功能拆分为多个单一、简单的功能,不同单一简单功能组合在一起,和未拆分前完成的功能是一样的。由于每个功能职责单一、简单,使得维护和变更都变得更简单、容易、安全,所以更易于产品版本的迭代,还能够快速的进行敏捷发布和上线。——微服务:微服务架构就是将一个完整的应用从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如RESTful API的方式互相调用。
19.LRU java实现:
@Test
public void test02(){
LRUCache<String, String> cache = new LRUCache<String, String>(5);
cache.put("1", "1");
cache.put("2", "2");
cache.put("3", "3");
cache.put("4", "4");
cache.put("5", "5");
System.out.println("初始化:");
System.out.println(cache.keySet());
System.out.println("访问3:");
cache.get("3");
System.out.println(cache.keySet());
System.out.println("访问2:");
cache.get("2");
System.out.println(cache.keySet());
System.out.println("增加数据6,7:");
cache.put("6", "6");
cache.put("7", "7");
System.out.println(cache.keySet());
}
// 利用现有的java数据结构来是实现 LRU算法
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int CACHE_SIZE;
/**
* 传递进来最多能缓存多少数据
*
* @param cacheSize 缓存大小
*/
public LRUCache(int cacheSize) {
// true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
return size() > CACHE_SIZE;
}
}
20.JDK、JRE、JVM三者间的关系
- JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE、Java工具和Java基础类库。
- Java Runtime Environment(JRE)是运行JAVA程序所必须的环境的集合,包含JVM标准实现及Java核心类库 。
- JVM是Java Virtual Machine(Java虚拟机)的缩写,是整个java实现跨平台的最核心的部分,能够运行以Java语言写作的软件程序。
21.Spring AOP 的切点的定义
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
在多个表达式之间使用 ||,or表示 或,使用 &&,and表示 与,!表示 非.例如:
<aop:config>
<aop:pointcut id="pointcut" expression="(execution(* com.ccboy.dao..*.find*(..))) or (execution(* com.ccboy.dao..*.query*(..)))"/>
<aop:advisor advice-ref="jdbcInterceptor" pointcut-ref="pointcut" />
</aop:config>
以上是关于java基础知识的主要内容,如果未能解决你的问题,请参考以下文章