CORS\CSRF\XSS
CORS : Cross Origin Resourse-Sharing 跨站资源共享
CSRF : Cross-Site Request Forgery 跨站请求伪造
XSS : Cross Site Scrit 跨站脚本攻击(为与 CSS 区别,所以在安全领域叫 XSS)
Split 加 \\ 总是保险的
public static void main(String[] args) {
//加 // 是保险的做法
String str = "111.222.333";
System.out.println(str.split("\\.").length);//ok
System.out.println(str.split(".").length);//不ok
System.out.println(str.split("//.").length);//不ok
str = "111,222,333";
System.out.println(str.split(",").length);//ok
System.out.println(str.split("\\,").length);//ok
str = "111/222/333";
System.out.println(str.split("\\/").length);//ok
System.out.println(str.split("//").length);//不ok
}
Java 数字支持中间加下划线1_1_1,不影响数值
public static void main(String[] args) {
System.out.println(1_1_1==111);//true
}
Java 默认数组最大长度限制
默认数组最大值为 Integer.Max = ,实际上由于不同JDK版本,数组最大长度都要小于这个值。
如果初始化的值过大,会报错。
比如 byte[] bitMap = new byte[(1<<31)]; 会导致报错
java.lang.NegativeArraySizeException
Arrays 的复制
public static void main(String[] args) {
int[] a = new int[]{1,2,3};
int[] b = new int[3];
int[] c = new int[3];
b=Arrays.copyOf(a,3);//复制的长度 [0,3)
c=Arrays.copyOfRange(a,2,3);//复制区间 [1,3)
System.out.println(JSONObject.toJSON(a));
System.out.println(JSONObject.toJSON(b));
System.out.println(JSONObject.toJSON(c));
}
int[] a2 = new int[]{1,2,3};
int[] d = new int[3];
//Object src, int srcPos,dest, int destPos,int length
System.arraycopy(a2,1,d,1,1);//[0,2,0] a2从index1开始,给d,从index1开始,复制1个
System.out.println(JSONObject.toJSON(d));
Arrays 的升序和降序
Integer[] array = new Integer[]{2,6,7,5,0}; Arrays.sort(array);//升序 (可以是非包装类型) Arrays.sort(array, Collections.reverseOrder());//升序+反转-降序:必须是包装类型
import com.alibaba.fastjson.JSONObject;
import java.util.Arrays;
import java.util.Collections;
public static void main(String[] args) {
Integer[] array = new Integer[]{2,6,7,5,0};
Arrays.sort(array);//升序
System.out.println(JSONObject.toJSON(array));//[0,2,5,6,7]
Arrays.sort(array, Collections.reverseOrder());//升序+反转-降序:必须是包装类型
System.out.println(JSONObject.toJSON(array));//[7,6,5,2,0]
}
Java do{}while()使用
if(compareTime==1){ //break; 会导致 do...while 中断 //continue; 会导致 do...while 中断 }
public static void main(String[] args) {
int compareTime = 0;
boolean compareResult = false;
int compareMaxTime = 3;
do{
if(compareTime==1){
compareTime=2;
//break; //会导致 do...while 中断
//continue; //会跳过本次循环,进入下一轮判断
//return; //会导致 do...while 中断
}
System.out.println("运行开始 compareTime="+compareTime);
compareResult = demo(compareTime);
compareTime++;
System.out.println("运行结束 compareTime="+compareTime);
}
while(!compareResult && compareTime<compareMaxTime);
System.out.println("运行结束 compareTime="+compareTime);
}
public static boolean demo(int compareTime){
if(compareTime==2){
return true;
}
return false;
}
运行开始 compareTime=0
运行结束 compareTime=1
运行开始 compareTime=1
运行结束 compareTime=2
运行开始 compareTime=2
运行结束 compareTime=3
运行结束 compareTime=3
Java组件类Triple、MutableTriple、ImmutableTriple存储三个对象类详解(二十七)Java组件类Triple、MutableTriple、ImmutableTriple存储三个对象类详解_艾米莉Emily的博客-CSDN博客_java triple组件类是在包org.apache.commons.lang3.tuple下;1.Triple抽象类,实现这个抽象类的类能够存储三个对象package org.apache.commons.lang3.tuple;import java.io.Serializable;import java.util.Objects;import org.apache.commons.lan...
https://blog.csdn.net/yaomingyang/article/details/79306103
Collections.emptyList
注意,这种方式生命的List虽然不是null,但是同样不支持add 和 remove
Objects.nonNull
Objects.nonNull 无法判断集合对象是不是空集合
Objects.nonNull 无法判断空字符串
List list = Lists.newArrayList();
System.out.println(Objects.nonNull(list));//true
System.out.println(CollectionUtils.isNotEmpty(list));//false
System.out.println(Objects.isNull(""));//false
随机数
Random 不太好,Math.random 好用
for(int i=0;i<100;i++){
//System.out.println(new Random(BizSysConstants.THREE_VALUE).nextInt(BizSysConstants.CACHE_ACCOUNT_MOD_VALUE));
System.out.println((int) (Math.random()*BizSysConstants.FIVE_VALUE));
}
String 的 compare
字符串每个首字母做减法
@Test
public void test1(){
String a = "9.1.1";
String b = "700";
String c = "6000";
String e = "100";
String f = "你";
System.out.println(a.compareTo(b));
System.out.println(a.compareTo(c));
System.out.println(a.compareTo(e));
System.out.println(a.compareTo(f));
}
2
3
8
-20263
打印日志-打印异常堆栈信息
方法有两个,第一个是使用 逗号 + e,
log.error("自定义描述信息",e);
错误的方法有 log.error("自定义描述信息"+e);
第二个方法是,直接将异常信息流转化为字符串
ByteArrayOutputStream byteArrayOutputStream = null;
PrintStream printStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
printStream = new PrintStream(byteArrayOutputStream);
ex.printStackTrace(printStream);
String reason = byteArrayOutputStream.toString();
System.out.println("path:"+ResolverUtil.getFuncName(point)+";reason:"+reason);
} catch (Exception e) {
log.error("error", e);
}finally {
try {
if(byteArrayOutputStream != null){
byteArrayOutputStream.close();
}
if(printStream != null){
printStream.close();
}
} catch (IOException e) {
log.error("error IOException", e);
}
}
List 类型取交集
输出:
false
list1:[1,2,3]
list2:[4,1,2,3]
public static void main(String[] args) {
List list1= Lists.newArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
List list2= Lists.newArrayList();
list2.add(4);
list2.add(1);
list2.add(2);
list2.add(3);
//list1 保留 list1 和 list2 的交集,如果 list1.size()长度变化,返回 true
boolean result= list1.retainAll(list2);
System.out.println(result);
System.out.println("list1:"+ JSON.toJSONString(list1));
System.out.println("list2:"+ JSON.toJSONString(list2));
}
代码注释引用其他代码-see 或者 @link
/**
* @see #变量名
* demo {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor} is aaa
*/
替换转移字符 合规 json
可能需要循环多次进行
String str = "{\\\"name\\\":\\\"spy\\\",\\\"id\\\\":\\\"123456\\\"}";
System.out.println("原始 str = " + str);
String str1 = StringEscapeUtils.unescapeJava(str);
System.out.println("目标 str1 = " + str1);
//对应方法的StringEscapeUtils.escapeJava(str1);
//可将str1转义回str
字符串、数组、集合相互之间的转化
/**
* 字符串、数组、集合相互之间的转化
* 字符串转数组:5
* 数组转集合:[1, 2, 3, 4, 5]
* 集合转数组:5
* @param args
*/
public static void main(String[] args) {
String str="1,2,3,4,5";
//字符串转数组
String[] arrays=str.split(",");
System.out.println("字符串转数组:"+arrays.length);
/* 数组转 集合 */
List<String> arrayList= Arrays.asList(arrays);
System.out.println("数组转集合:"+arrayList);
//集合转数组
arrays=(String[]) arrayList.toArray();
System.out.println("集合转数组:"+arrays.length);
List<String> list = Lists.newArrayList("111","222","333");
//lamada 表达式遍历 list
list.stream().forEach(str->{
System.out.println( str);
});
//lamada 表达式遍历 map
Map map=new HashMap<>();
map.forEach((k,v)->{
System.out.println(k+":"+v);
});
}
·isAssignableFrom()方法与instanceof关键字
1、instanceof 用于判断A 是不是B 的子类
boolean result=A instanceof B; 如果A是B的子类,则result为true,B可以是类或者接口,A可以是类或者接口
2、isAssignableFrom() 用于判断 A 是不是 B的父类
父类.class.isAssignableFrom(子类.class)
子类实例 instanceof 父类类型
Class<?> contextClass = determineContextClass(sc);
boolean result2=ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass);
class Class2 extends TestClass{
}
public class TestClass {
public static void main(String[] args) {
System.out.println(TestClass.class.isAssignableFrom(Class2.class));//true,Class2是 TestClass 的子类
}
如果 result2 为true,则表示 ConfigurableWebApplicationContext 是 contextClass 的父类,上面是Spring源码的内容
·i++ 和 ++i
i=i++; i会保持原值不变,i=i--;i会保持原值不变
i++ 运行时,先获取i的值,在操作 i+1,++i是i先加1在获取i的值
比如下面的代码,i初始值为0,i++输出0,i变为1,++i,i变为2,然后输出2
那么 int j=i++ + ++i=2 ,这个步骤就是j=0+2=2
volatile static int i=0;
//int i=0;
public static void main(String[] args) {
//i++ 会先取i,然后 i=i+1;++i 会先 i=i+1,然后取i
//和 volatile 无关呦
System.out.println(i++ +";"+ ++i);//0,2
}
看一个更明显的例子
public static void main(String[] args) {
int i=0;
System.out.println(i++);//0
System.out.println(i);//1
}
· try、catch、finally 和 return 的关系
0、如果finally中有 return,则会直接运行这个return语句,所以不要在finally中写 return,可以在cache中写,finally用于资源回收
1、try和catch先运行finally会在最后运行,无论三个代码块是否有 return
2、如果return 内容是值传递,则finally 仅修改值无法修改上面代码块的赋值结果,比如 String a;return a;
3、如果return 内容是引用传递,则finally 修改值后会修改最后的返回结果,比如 List list;retuan list;
@Test
public void test1() {
System.out.println(get());//值传递测试
// try
// catch
// finally
// 2
System.out.println(getList());//地址传递测试
// try
// catch
// finally
// [3]
}
//引用传递 finally 修改值影响返回结果
public List getList() {
List a=new ArrayList();
a.add("0");
try {
System.out.println("try");
a.set(0, "1");
if(true) {
throw new Exception();
}
return a;
}
catch(Exception e) {
System.out.println("catch");
a.set(0, "2");
return a;
}finally {
System.out.println("finally");
a.set(0, "3");
//return "3";//有return 以 return 为主
}
}
//值传递 finally修改值不影响返回结果
public String get() {
String a="0";
try {
System.out.println("try");
a="1";
if(true) {
throw new Exception();
}
return a;
}
catch(Exception e) {
System.out.println("catch");
a="2";
return a;
}finally {
System.out.println("finally");
a="3";
//return "3";//有return 以 return 为主
}
}
catch、finally 同时抛出异常,仅fianlly 中抛出异常生效
catch 和 finally 同时抛出异常,只有finally 中抛出异常会生效,相当于 抛出异常有return的效果,所以一般而言不要在 finally 语句中抛出异常
public class Test {
static class MyException extends Exception {
String tag;
public MyException(String tag) {
this.tag = tag;
System.out.println("tag="+tag);
}
}
public static void main(String[] args) throws MyException {
try {
System.out.println("1");
} catch (Exception e) {
throw new MyException("2");
} finally {
throw new MyException("3");
}
}
}
输出结果如下(catch 代码块未运行)
1
tag=3
Exception in thread "main" del.log.Test$MyException
at del.log.Test.main(Test.java:31)
·JVM 自带命令行
jps 查看jvm的进程,jstack 查看具体某一个进程的堆栈跟踪信息
·JVM 自带工具
JConsole、jvisualvm
·BufferedImage 转化为 InputStream
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
BufferedImage image;
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "JPG", os);
InputStream inputStream = new ByteArrayInputStream(os.toByteArray());
·docker 无法加载 jar 中文件解决
InputStream inputStreamSource = getClass().getClassLoader().getResourceAsStream("static/aaa.xlsx");
File targetFile = new File("xxx.pdf");
FileUtils.copyInputStreamToFile(stream, targetFile);
·Workbook 转化为 InputStream
private static InputStream createNewFile2(Map<String, Object> map, File file) {
XLSTransformer transformer = new XLSTransformer();
String name = "demo_create.xlsx";
String path = "C://";
try (
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
ByteArrayOutputStream os = new ByteArrayOutputStream();
) {
Workbook workbook = transformer.transformXLS(inputStream, map);
workbook.write(os);
InputStream inputStream2 = new ByteArrayInputStream(os.toByteArray());
return inputStream2;
} catch (FileNotFoundException | InvalidFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
·Java File 类中本地文件的 剪切功能
File file=new File("/路径/0000111.jpg");
file.renameTo(new File("/路径/test/0000111.jpg"));
·感受一个 byte (比特)就是一位,bytes[] 就是很多位
public static void main(String [] args){
byte[] bytes=new byte[3];
System.out.println(new String(bytes));//
}
· Java 换行符
System.out.print("1"+System.getProperty("line.separator")+"2");
replaceAll("[\\t\\n\\r]", "")
·Jdk 提供的 高性能对象 toString 方法
方式一:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
方式二:
但是如果遇到掩码修饰的则有问题,可以用下面的方法
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
简单用法
@Test
public void testFastJosn(){
UserInfo userInfo = new UserInfo();
userInfo.setId(1L);
userInfo.setUsername("jecket");
System.out.println(JSON.toJSONString(userInfo));
}
输出:{"id":1,"username":"jecket"}
如果需要对字符进行处理,则可以使用复杂用法:
import com.alibaba.fastjson.JSON;
@Override
public String toString() {
return JSON.toJSONString(this, new ValueFilter() {
@Override
public Object process(Object object, String name, Object value) {
if ("cardNo".equals(name)) {
return getShieldParam(name,(String)value);
}
return value;
}
});
}
/**
* 对参数进行掩码处理
* 后期如果有新对参数需要掩码则对该方法进行扩展
*/
private String getShieldParam(String cardNo,String value) {
if("cardNo".equals(cardNo)) {
return "**** **** ****"+(StringUtils.isEmpty(value)?"***":value.substring(value.length()-4,value.length()));
}
return "";
}
如果需要在web项目中使用,则还需要一个额外的操作
·你不能创建不可具体化的类型的数组
你不能创建不可具体化的类型的数组,反例如下:E[] array=new E[1];可以写成new (E[])Object[1],或者将array域的类型改为Object[] ,举一个很简单的例子就可以明白,如果E是接口的话,其不能出现在右侧。
·Java 原码、反码和补码
·Java 八种基本类型
byte 类型是 8 位,即1个字节,对应的装箱类型为 Byte,取值范围为:
装箱类型表示:-128到127
位移运算表示:-128到127
short 类型是16位,即2个字节,对应的装箱类型为 Short,取值范围为:
装箱类型表示:-32768;32767
位移运算表示:-32768;32767
int 类型是 32 位,即4个字节,对应的装箱类型为 Integer,取值范围为:
装箱类型表示:-2147483648到2147483647
位移运算表示:-2147483648到2147483647
long 类型是 64位,即8个字节,对应的装箱类型为 Long,取值范围为:
装箱类型表示:-9223372036854775808到9223372036854775807
Java 中 位移运算最多表示32位,无法表示long类型
float 类型是 16 位,即2个字节,对应的装箱类型为 Float,取值范围(仅正数部分,负数需加负号)为:
装箱类型表示:1.4E-45到3.4028235E38
1.4E-45表示 1.4乘以10的负45次幂;3.4028235E38 表示 3.4028235 乘以10的38次幂,那么0到1.4E-45 表示不出来
精度小于1.4E-45,失去精度结果为0.0,new Float(1.4E-46)=0.0;所以涉及金额等需要使用BigDecimal
double 类型是 32 位,即4个字节,对应的装箱类型为 Double,取值范围(仅正数部分,负数需加负号)为:
装箱类型表示:4.9E-324到1.7976931348623157E308
4.9E-324表示 4.9乘以10的负324次幂;1.7976931348623157E308 表示 1.7976931348623157 乘以10的308次幂,那么0到4.9E-324 表示不出来
精度小于4.9E-324 IDE报错,new Double(4.9E-325);所以涉及金额等需要使用BigDecimal
boolean 类型是 1 位,即1/8个字节,对应的装箱类型为 Boolean:取值为 true 或者 false,但是具体实现上可能是1个字节或4个字节,从计算机寻址角度看应该是字节的整数倍.
char 类型是 16 位,即2个字节,对应的装箱类型为Character,他很神奇,以后再做深入了解
测试代码:
@Test
public void test1(){
System.out.println("byte 类型是 8 位,即1个字节,对应的装箱类型为 Byte,取值范围为:");
System.out.println("装箱类型表示:"+Byte.MIN_VALUE+"到"+Byte.MAX_VALUE);//-128到127
System.out.println("位移运算表示:"+-(1<<7)+"到"+((1<<7)-1));//-(1<<7)到((1<<7)-1)
System.out.println();
System.out.println("short 类型是16位,即2个字节,对应的装箱类型为 Short,取值范围为:");
System.out.println("装箱类型表示:"+Short.MIN_VALUE+";"+Short.MAX_VALUE);//-32768;32767
System.out.println("位移运算表示:"+-(1<<15)+";"+((1<<15)-1));//-(1<<15)到((1<<15)-1)
System.out.println();
System.out.println("int 类型是 32 位,即4个字节,对应的装箱类型为 Integer,取值范围为:");
System.out.println("装箱类型表示:"+Integer.MIN_VALUE+"到"+Integer.MAX_VALUE);//-2147483648到2147483647
System.out.println("位移运算表示:"+-(1<<31)+"到"+((1<<31)-1));//-(1<<31)到((1<<31)-1)
System.out.println();
System.out.println("long 类型是 64位,即8个字节,对应的装箱类型为 Long,取值范围为:");
System.out.println("装箱类型表示:"+Long.MIN_VALUE+"到"+Long.MAX_VALUE);//-2147483648到2147483647
System.out.println("Java 中 位移运算最多表示32位,无法表示long类型");
System.out.println();
System.out.println("float 类型是 16 位,即2个字节,对应的装箱类型为 Float,取值范围(仅正数部分,负数需加负号)为:");
System.out.println("装箱类型表示:"+Float.MIN_VALUE+"到"+Float.MAX_VALUE);//1.4E-45到3.4028235E38
System.out.println("1.4E-45表示 1.4乘以10的负45次幂;3.4028235E38 表示 3.4028235 乘以10的38次幂,那么0到1.4E-45 表示不出来");
System.out.println("精度小于1.4E-45,失去精度结果为0.0,new Float(1.4E-46)="+new Float(1.4E-46)+";所以涉及金额等需要使用BigDecimal");
System.out.println();
System.out.println("double 类型是 32 位,即4个字节,对应的装箱类型为 Double,取值范围(仅正数部分,负数需加负号)为:");
System.out.println("装箱类型表示:"+Double.MIN_VALUE+"到"+Double.MAX_VALUE);//4.9E-324到1.7976931348623157E308
System.out.println("4.9E-324表示 4.9乘以10的负324次幂;1.7976931348623157E308 表示 1.7976931348623157 乘以10的308次幂,那么0到4.9E-324 表示不出来");
System.out.println("精度小于4.9E-324 IDE报错,new Double(4.9E-325);所以涉及金额等需要使用BigDecimal");
System.out.println();
System.out.println("boolean 类型是 2 位,即1/8个字节,对应的装箱类型为 Boolean:取值为 true 或者 false");
System.out.println();
System.out.println("char 类型是 16 位,即2个字节,对应的装箱类型为Character,他很神奇,以后再做深入了解:");
System.out.println(Character.MIN_CODE_POINT+"到"+Character.MAX_CODE_POINT);
System.out.println(Character.MIN_HIGH_SURROGATE+"到"+Character.MAX_HIGH_SURROGATE);//UTF-16 大写
System.out.println(Character.MIN_LOW_SURROGATE+"到"+Character.MAX_LOW_SURROGATE);//UTF-16 小写
System.out.println(Character.MIN_RADIX+"到"+Character.MAX_RADIX);
System.out.println(Character.MIN_SUPPLEMENTARY_CODE_POINT);//Unicode
System.out.println(Character.MIN_SURROGATE);
System.out.println(Character.MIN_VALUE);
}
·Java 中常见的几个类型是值传递还是引用传递的判断
1、Java常用类型的值传递和引用传递
所谓的值传递和引用传递的实质上的场景是这样的。
比如参数a是void 修饰的方法1的一个入参,如果在方法1中修改入参的a的值,然后在方法1外读取原始的参数1,参数1的值是否改变,
如果参数a依旧是原始值,则说a是值传递,这是把a的值传递进入了,如果a的值与原始值不同了,则说a传递的是一个地址,
方法1修改的是参数a指向的内存地址。
2、意义:入参是否是值传递会影响后续代码的结果,所以有必要搞清楚
3、常见类型的传递类型结论:
数组 是引用传递
POJO 是引用传递
List 是引用传递
Map 是引用传递
Set 是引用传递
八种基本类型 byte、short、int、long、boolean、char、float、double 是值传递
八种基本类型对应的包装类型 Byte、Short、Integer、Long、Boolean、Character、Float、Double 是值传递
String 类 是值传递,不区分声明方式
Date 是值传递
测试代码:
@Test
public void test3(){
System.out.println("Set 类型引用传递测试:");
Set<Integer> set=new HashSet<Integer>();
set.add(1);
System.out.println("set.get(0)="+set.toString()+",调用 方法test3_9,");
test3_9(set);
System.out.println("set.get(0)="+set.toString());
System.out.println("方法内修改 Set 类型 参数值 会修改原始参数值,所以 Set 类型是地址传递\n");
System.out.println("Map 类型引用传递测试:");
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("key", 1);
System.out.println("map.get(\"key\")="+map.get("key")+",调用 方法test3_8,");
test3_8(map);
System.out.println("map.get(\"key\")="+map.get("key"));
System.out.println("方法内修改 Map 类型 参数值 会修改原始参数值,所以 Map 类型是地址传递\n");
System.out.println("Java 8 种基本类型对应的包装类型 Byte、Short、Integer、Long、Boolean、Character、Float、Double 类型引用传递测试:");
Byte x1=1;
Short x2=1;
Integer x3=1;
Long x4=1L;
Boolean x5=true;
Character x6=1;
Float x7=1.0f;
Double x8=1.0;
System.out.println("Byte x1="+x1+
";Short x2="+x2+
";Integer x3="+x3+
";Long x4="+x4+
";Boolean x5="+x5+
";Character x6="+x6+
";Float x7="+x7+
";Double x8="+x8+";调用方法test3_7");
test3_7(x1,x2,x3,x4,x5,x6,x7,x8);
System.out.println("Byte x1="+x1+
";Short x2="+x2+
";Integer x3="+x3+
";Long x4="+x4+
";Boolean x5="+x5+
";Character x6="+x6+
";Float x7="+x7+
";Double x8="+x8);
System.out.println("方法内修改 Java 8 种基本类型对应的包装类型 Byte、Short、Integer、Long、Boolean、Character、Float、Double 类型 参数值, 原始参数值不变,所以 Java八种基本类型对应的包装类型是值传递\n");
System.out.println("Java 8种基本 byte、short、int、long、boolean、char、float、double 类型引用传递测试:");
byte s1=1;
short s2=1;
int s3=1;
long s4=1;
boolean s5=true;
char s6=1;
float s7=1.0f;
double s8=1.0;
System.out.println("byte s1="+s1+
";short s2="+s2+
";int s3="+s3+
";long s4="+s4+
";boolean s5="+s5+
";char s6="+s6+
";float s7="+s7+
";double s8="+s8+";调用方法test3_6");
test3_6(s1,s2,s3,s4,s5,s6,s7,s8);
System.out.println("byte s1="+s1+
";short s2="+s2+
";int s3="+s3+
";long s4="+s4+
";boolean s5="+s5+
";char s6="+s6+
";float s7="+s7+
";double s8="+s8);
System.out.println("方法内修改 Java 8种基本 byte、short、int、long、boolean、char、float、double 类型 参数值, 原始参数值不变,所以 Java八种基本 类型是值传递\n");
System.out.println("List 类型引用传递测试:");
List<Integer> list=new ArrayList<Integer>();
list.add(0,1);
System.out.println("list.get(0)="+list.get(0)+",调用 方法test3_5,");
test3_5(list);
System.out.println("list.get(0)="+list.get(0));
System.out.println("方法内修改 list 类型 参数值 会修改原始参数值,所以 List 类型是地址传递\n");
System.out.println("Date 类型引用传递测试:");
Date date=new Date();
System.out.println("date.getTime()="+date.getTime()+",调用 方法test3_4,");
test3_4(date);
System.out.println("date.getTime()="+date.getTime());
System.out.println("方法内修改 Date 类型 参数值不会修改原始参数值,所以 Date 类型是值传递\n");
System.out.println("简单 Java对象 (POJO) 类型引用传递测试:");
User u=new User();
u.setA(1.1);
System.out.println("u.getA()="+u.getA()+",调用方法test3_3,");
test3_3(u);
System.out.println("u.getA()="+u.getA());
System.out.println("方法内修改 简单 Java 对象(POJO) 类型 参数值会修改原始参数值,所以 简单 Java 对象(POJO) 类型是地址传递\n");
System.out.println("String 类型引用传递测试:");
String b="012";
String c=new String("012");
System.out.print("b="+b);
System.out.println(";c="+c+";调用方法test3_1,");
test3_1(b);
test3_1(c);
System.out.print("b="+b);
System.out.println(";c="+c);
System.out.println("方法内修改 String 类型参数值未修改原始参数值,所以String 类型是值传递,不区分 String 的声明方式为直接赋值或者new\n");
System.out.println("数组 类型引用传递测试:");
String[] str2=new String[3];
str2[0]="012";
System.out.println("str2[0]="+str2[0]+",调用方法test3_2,");
test3_2(str2);
System.out.println("str2[0]="+str2[0]);
System.out.println("方法内修改 数组 类型参数值会修改原始参数值,所以 数组 类型是地址传递");
}
public void test3_1(String b){
b="123";
}
public void test3_2(String[] b){
b[0]="123";
}
public void test3_3(User u){
u.setA(1.2);
}
public void test3_4(Date date){
date=new Date(date.getTime()+1000);
}
public void test3_5(List<Integer> list){
list.set(0, 2);
}
public void test3_6(byte s1,
short s2,
int s3,
long s4,
boolean s5,
char s6,
float s7,
double s8){
s1=2;
s2=2;
s3=2;
s4=2;
s5=false;
s6=2;
s7=2.0f;
s8=2.0;
}
public void test3_7(Byte s1,
Short s2,
Integer s3,
Long s4,
Boolean s5,
Character s6,
Float s7,
Double s8){
s1=2;
s2=2;
s3=2;
s4=2L;
s5=false;
s6=2;
s7=2.0f;
s8=2.0;
}
public void test3_8(Map<String,Integer> map){
map.put("key", 2);
}
public void test3_9(Set<Integer> set){
set.add(2);
}
测试代码运行结果:
Set 类型引用传递测试:
set.get(0)=[1],调用 方法test3_9,
set.get(0)=[1, 2]
方法内修改 Set 类型 参数值 会修改原始参数值,所以 Set 类型是地址传递
Map 类型引用传递测试:
map.get("key")=1,调用 方法test3_8,
map.get("key")=2
方法内修改 Map 类型 参数值 会修改原始参数值,所以 Map 类型是地址传递
Java 8 种基本类型对应的包装类型 Byte、Short、Integer、Long、Boolean、Character、Float、Double 类型引用传递测试:
Byte x1=1;Short x2=1;Integer x3=1;Long x4=1;Boolean x5=true;Character x6=;Float x7=1.0;Double x8=1.
0;调用方法test3_7
Byte x1=1;Short x2=1;Integer x3=1;Long x4=1;Boolean x5=true;Character x6=;Float x7=1.0;Double x8=1.
0
方法内修改 Java 8 种基本类型对应的包装类型 Byte、Short、Integer、Long、Boolean、Character、Float、Double 类型 参数值, 原始参数值不变,所以
Java八种基本类型对应的包装类型是值传递
Java 8种基本 byte、short、int、long、boolean、char、float、double 类型引用传递测试:
byte s1=1;short s2=1;int s3=1;long s4=1;boolean s5=true;char s6=;float s7=1.0;double s8=1.0;调用方法tes
t3_6
byte s1=1;short s2=1;int s3=1;long s4=1;boolean s5=true;char s6=;float s7=1.0;double s8=1.0
方法内修改 Java 8种基本 byte、short、int、long、boolean、char、float、double 类型 参数值, 原始参数值不变,所以 Java八种基本 类型是值传递
List 类型引用传递测试:
list.get(0)=1,调用 方法test3_5,
list.get(0)=2
方法内修改 list 类型 参数值 会修改原始参数值,所以 List 类型是地址传递
Date 类型引用传递测试:
date.getTime()=1514621236870,调用 方法test3_4,
date.getTime()=1514621236870
方法内修改 Date 类型 参数值不会修改原始参数值,所以 Date 类型是值传递
简单 Java对象 (POJO) 类型引用传递测试:
u.getA()=1.1,调用方法test3_3,
u.getA()=1.2
方法内修改 简单 Java 对象(POJO) 类型 参数值会修改原始参数值,所以 简单 Java 对象(POJO) 类型是地址传递
String 类型引用传递测试:
b=012;c=012;调用方法test3_1,
b=012;c=012
方法内修改 String 类型参数值未修改原始参数值,所以String 类型是值传递,不区分 String 的声明方式为直接赋值或者new
数组 类型引用传递测试:
str2[0]=012,调用方法test3_2,
str2[0]=123
方法内修改 数组 类型参数值会修改原始参数值,所以 数组 类型是地址传递
·Java中的位移运算:<<、>>和>>>
十进制转2进制="+Integer.toBinaryString(-2)); //11111111111111111111111111111110
十进制转2进制="+Integer.toBinaryString((-2)>>>1)); //1111111111111111111111111111111
>>> 无符号位移,以补码为准进行移动,移动之后不考虑第一位是不是符号位,其实是简化了问题
在使用位移运算的时候,注意java中整形默认是 int 类型的,即32位,再一个,java中数字使用机器数中的补码表示,所以注意位移运算的范围和变换的机器码形式。
使用 原码 11100000 00000000 00000000 00000000 验证负数的位移和无符号位移运算,可以得出结论java使用是在补码的形式上进行位移操作的
正数左位移n位相当于真值乘以2的n次幂,整体左移n位,高位舍弃n位,右边补n个0
正数右位移n位相当于真值除以2的n次幂,整体右移n位,末尾舍弃n位,左边补n个0
负数左移n位无法使用乘除描述,整体左移n位,高位舍去n位,低位补n个0
负数右移n位无法使用乘除描述,整体右移n位,低位舍去n位,高位补n个1
无符号右移n位无法使用乘除描述,整体右移n位,低位舍去n位,高位补n个零,无符号就是高位一致补零
1<<1:1左位移一位得 2:2正数左位移1位相当于真值乘以2的一次幂,整体左移,高位舍弃,右边补0
2>>1:2右位移一位得 1:1正数右位移1位相当于真值除以2的一次幂,整体右移,末尾舍弃,左边补0
(-1)<<1:-1左位移一位得 -2:-2负数左移1位无法使用乘除描述,整体左移,高位舍去,低位补0
(-1)>>1:-1右位移一位得 -1:-1负数右移1位无法使用乘除描述,整体右移,低位舍去,高位补1
(-1)>>>1:-1无符号右位移一位得 -1:2147483647无符号右移1位无法使用乘除描述,整体右移,低位舍去,高位补0,无符号就是高位补零的无符号
代码:
@Test
public void test2(){
System.out.println("在使用位移运算的时候,注意java中整形默认是 int 类型的,即32位,再一个,java中数字使用机器数中的补码表示,所以注意位移运算的范围和变换的机器码形式。");
System.out.println("使用 原码 11100000 00000000 00000000 00000000 验证负数的位移和无符号位移运算,可以得出结论java使用是在补码的形式上进行位移操作的");
System.out.println("正数左位移n位相当于真值乘以2的n次幂,整体左移n位,高位舍弃n位,右边补n个0");
System.out.println("正数右位移n位相当于真值除以2的n次幂,整体右移n位,末尾舍弃n位,左边补n个0");
System.out.println("负数左移n位无法使用乘除描述,整体左移n位,高位舍去n位,低位补n个0");
System.out.println("负数右移n位无法使用乘除描述,整体右移n位,低位舍去n位,高位补n个0");
System.out.println("无符号右移n位无法使用乘除描述,整体右移n位,低位舍去n位,高位补n个零,无符号就是高位一致补零");
System.out.print("1<<1:1左位移一位得 2:");System.out.print(1<<1);System.out.println("正数左位移1位相当于真值乘以2的一次幂,整体左移,高位舍弃,右边补0");
System.out.print("2>>1:2右位移一位得 1:");System.out.print(2>>1);System.out.println("正数右位移1位相当于真值除以2的一次幂,整体右移,末尾舍弃,左边补0");
System.out.print("(-1)<<1:-1左位移一位得 -2:");System.out.print((-1)<<1);System.out.println("负数左移1位无法使用乘除描述,整体左移,高位舍去,低位补0");
System.out.print("(-1)>>1:-1右位移一位得 -1:");System.out.print((-1)>>1);System.out.println("负数右移1位无法使用乘除描述,整体右移,低位舍去,高位补1");
System.out.print("(-1)>>>1:-1无符号右位移一位得 -1:");System.out.print((-1)>>>1);System.out.println("无符号右移1位无法使用乘除描述,整体右移,低位舍去,高位补0,无符号就是高位补零的无符号");
}
·基本类型优先,装箱基本类型使用注意事项
-出于性能的考虑,要优先使用基本类型,而不是装箱基本类型,自动拆箱和装箱会降低性能。
-使用装箱基本类型要注意两点:1、两个装箱基本类型使用<或者>会自动拆箱为基本类型比较大小,用==比较则是同一性比较,即比较两个装箱基本类型是不是同一个对象。2、装箱基本类型实质为对象,如果是空对象,在自动拆箱的时候会报空指针异常。
-什么时候使用装箱基本类型?集合中的元素,键和值,如ListArray<Integer>。或者进行反射方法的调用。
·字节输入流和字符输入流,字节输出流和字符输出流
在Java语言中,所有的输入流都是从抽象类InputStream和Reader类继承而来,它们分别是所有字节输入流和字符输入流的父类。所有输出流都是从OutputStream和Write类继承而来,它们是所有字节输出流和字符输出流的父类。RandomAccessFile类继承了Object类并实现了DateInput和DataOutput接口,它主要用来随机访问文件。扩展Object类的StreamTokenizer可以用于分析文本文件。
·自动装箱和自动拆箱
Java 有一个系统由两部分组成,包含基本类型和引用类型,每个基本类型都有一个对应的引用类型,这个几个比较特殊的引用类型被称为装箱基本类型。比如int对应的引用类型(装箱基本类型)就是Integer。Java 1.5 发型版本中增加了自动装箱(autoboxing)和自动拆箱(auto-unboxing)。
基本类型只有值,而且基本类型更节省时间和空间。而引用类型除了值还有同一性,以及一个特殊的非功能值 null。
利用javap -c 认识自动装箱和自动拆箱
实验的.java文件:Hello.java
package com.bestcxx.stu.springmvc.common.test;
public class Hello {
public void test1(){
int a=2;
Integer b=a;//自动装箱
}
public void test2(){
Integer a=new Integer(2);
int b=a;//自动拆箱
}
}
javac Hello.java 得到 Hello.class
再通过运行 javap -c Hello.class 得到如下内容
public class com.bestcxx.stu.springmvc.common.test.Hello {
public com.bestcxx.stu.springmvc.common.test.Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test1();
Code:
0: iconst_2
1: istore_1
2: iload_1
3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
6: astore_2
7: return
public void test2();
Code:
0: new #3 // class java/lang/Integer
3: dup
4: iconst_2
5: invokespecial #4 // Method java/lang/Integer."<init>":(I)V
8: astore_1
9: aload_1
10: invokevirtual #5 // Method java/lang/Integer.intValue:()I
13: istore_2
14: return
}
其中 test1()对应 3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 表示int 赋值给 Integer 其实为 Integer.valueOf 即自动装箱
test2()对应 10: invokevirtual #5 // Method java/lang/Integer.intValue:()I ; 表示,其实 Integer 赋值给 int 其实为 Integer.intValue 即自动拆箱
·认识 javac 和javap -c
javac:编译 .java文件为.class 文件。
javap -c:针对 .class文件,输出类中各方法的未解析的代码,即构成 Java 字节码的指令。用于了解Java处理的细节,比如String创建了几个对象?自动装箱和拆箱。
用法简述:
javac:javac Hello.java 得到 Hello.class
javap -c:必须存在.class文件如javap -c Hello.class或者 javap -c Hello
·User.getDouble()!=User2.getDouble 实体属性为包装类型比较大小不能直接使用==
实体类型的属性为包装类型时,直接使用==会出问题,对于数值型需要获值(Double.doubleValue(),Integer.intValue()),或者覆盖重写equal方法
实体类
public class User {
private Double a;
private Integer b;
public Double getA() {
return a;
}
public void setA(Double a) {
this.a = a;
}
public Integer getB() {
return b;
}
public void setB(Integer b) {
this.b = b;
}
}
测试
import org.junit.Test;
import com.bestcxx.stu.fileup.model.User;
public class StringTest {
@Test
public void test(){
User u1=new User();
User u2=new User();
u1.setA(1.1);
u2.setA(1.1);
System.out.println(u1.getA()==u2.getA());//false
System.out.println(u1.getA().doubleValue()==u2.getA().doubleValue());//true
System.out.println(u1.getA().equals(u2.getA()));//true
u1.setB(1);
u2.setB(1);
System.out.println(u1.getB()==u2.getB());//true
System.out.println(u1.getB().intValue()==u2.getB().intValue());//true
System.out.println(u1.getB().equals(u2.getA()));//false
}
}
·运算级别 5/2+5%2>0?1:0 得1
5/2+5%2>0?1:0 得 1
5/2+(5%2>0?1:0)得3
Java有运算级别,记忆起来有些复杂,建议是 加括号和做充分测试
·java 2进制、十进制、8进制、16进制 和&、|、^、~运算
Java中不能直接表示2进制数。可以用字符串转为10进制
//二进制转10进制 Integer.parseInt("1010",2);
8进制数为0开头,16进制数为0x开头(x不区分大小写),直接书写即为相应进制的数,但是【字符串】格式转化时无需0(非强制)或者0x(强制)前缀
以十进制的 10 为基数,2、8、16进制与十进制之间的交换:
//方式一 是以 符号+数字 确定被转化数字为几进制度,默认是10进制,0开头是8进制,0x或0X 开头是十六进制
//方式二 是转10进制的情况,可以以数字的形式制定是几进制 Integer.parseInt("777", 8);//n 进制 转 十进制,这里是 八进制
public static void main(String[] args) {
// 10 十进制数
// 0数字 0开头是8进制
// 0x数组 0x或0X 开头是十六进制
System.out.println(Integer.toBinaryString(10));//十进制 转 二进制
//二进制字符串转10进制
Integer.parseInt("1010",2);
Integer.toBinaryString(0x999);// 十六进制 转 二进制
Integer.toBinaryString(0777);// 八进制 转 二进制
Integer.parseInt("777", 8);//n 进制 转 十进制,这里是 八进制
Integer.toOctalString(999);//十进制 转 八进制
Integer.toHexString(999);//十进制 转 十六进制
}
//Java Integer.toBinaryString 打印字符串,二进制补码默认为 32位,如果数字为负数则正好打印32位,如果为正数,则会以第一个1开始打印,如果为0就是0
//所以这里提供一个小工具方法,可以统一将二进制32位补码打印出来
private static String fullLengthTo32(int arg) {
String str = Integer.toBinaryString(arg);
int gap = 32 - str.length();
StringBuilder result = new StringBuilder();
for (int i = 0; i < gap; i++) {
result.append("0");
}
return result.append(str).toString();
}
待转化的十进制数为:10
十进制和2进制>>
十进制转2进制=1010
2进制 1010 转十进制=10
十进制和8进制>>
十进制转8进制=12
8进制 012 转十进制=10
十进制和16进制>>
十进制转16进制=a
16进制 0xA 转十进制=10
2、8、16进制之间的交换可以以十进制为中转。
Java &-与、|-或、^-异或、~-非运算,A&B 末位对齐都是1得1,可用于截取位数;A|B 末位对齐有一个1即得1否则得0;A^B 末位对齐相同得0,不同得1,可以认为是|A位值-B位值|;~A,非运算,1变0,0变1
10的二进制表示:1010
8的二进制表示:1000
10&8=1010&1000=二进制值=1000
10|8=1010|1000=二进制值=1010
10^8=1010^1000=二进制值=10
~10=~1010=二进制值-Integer 是32位结果很长=11111111111111111111111111110101
~10&15=~1010&1111=二进制值=(使用&运算,可以截取多余的位,1111对应15,故为 ~10&15)=101
测试代码
Integer.toBinaryString(10);//十进制转二进制
Integer.toBinaryString(010);//八进制转二进制
Integer.toBinaryString(0x10);//十六进制转二进制
Integer.parseInt(ox10)//转10进制
Integer.toOctalString(10)//转8进制
Integer.toHexString(10)//转16进制
import org.junit.Test;
/**
* Java 进制和位运算
* @author Administrator
*
*/
public class BitOperationTest {
/*
*第一部分:进制之间的转化
*/
/**
* 各个进制表示十进制的10
* 十进制 10
* 二进制 1010
* 八进制 012
* 十六进制 0xa 字母不区分大小写
*
* 字符串-转化为10进制-转化为其他进制
*/
@Test
public void test0(){
System.out.println("Java中不能直接表示2进制数,8进制数为0开头,16进制数为0x开头(x不区分大小写),直接书写即为相应进制的数"
+ ",但是【字符串】格式转化时无需0(非强制)或者0x(强制)前缀"
+ "\n以十进制的 10 为基数,2、8、16进制与十进制之间的交换:");
Integer number=10;
System.out.println();
System.out.println("待转化的十进制数为:"+number);
System.out.println("十进制和2进制>>");
//十进制转2进制
System.out.println(" 十进制转2进制="+Integer.toBinaryString(10));
//2进制转十进制
System.out.println(" 2进制 1010 转十进制="+Integer.parseInt("1010",2));
System.out.println("十进制和8进制>>");
//十进制转8进制
System.out.println(" 十进制转8进制="+Integer.toOctalString(10));
//8进制转十进制
System.out.println(" 8进制 012 转十进制="+Integer.parseInt("12",8));//字符格式转化无需前缀0,非强制
System.out.println("十进制和16进制>>");
//十进制转16进制
System.out.println(" 十进制转16进制="+Integer.toHexString(10));
//16进制转十进制
System.out.println(" 16进制 0xA 转十进制="+Integer.parseInt("A",16));//字符格式转化无需前缀0x,强制
System.out.println("2、8、16进制之间的交换可以以十进制为中转。");
}
/*
* 第二部分:与、或、异或、非运算
* A&B 末位对齐,都为1得1,否则得0,与1111与运算,可以保留4位,&运算可用于截位
* A|B 末位对齐,只要有一个为1即为1
* A^b 末位对齐,相同得0,不同得1
* ~A 单值,全部取反,如果是机器数则可得补码-java用补码表示机器数,且补码是2进制
*/
@Test
public void test1(){
System.out.println("\n Java &、|、^、~运算,A&B 末位对齐都是1得1,可用于截取位数;A|B 末位对齐有一个1即可;A^B 末位对齐相同得0,不同得1;~A,非运算,1变0,0变1");
System.out.println("10的二进制表示:"+Integer.toBinaryString(10)); //1010
System.out.println("8的二进制表示:"+Integer.toBinaryString(8)); //1000
//A&B 末位对齐,都为1得1,否则得0,与1111与运算,可以保留4位,&运算可用于截位
//A|B 末位对齐,只要有一个为1即为1
//A^b 末位对齐,相同得0,不同得1
//~A 单值,全部取反
System.out.println("10&8=1010&1000=二进制值="+Integer.toBinaryString(10&8));
System.out.println("10|8=1010|1000=二进制值="+Integer.toBinaryString(10|8));
System.out.println("10^8=1010^1000=二进制值="+Integer.toBinaryString(10^8));
System.out.println("~10=~1010=二进制值-Integer 是32位结果很长="+Integer.toBinaryString(~10));
System.out.println("~10&15=~1010&1111=二进制值=(使用&运算,可以截取多余的位,1111对应15,故为 ~10&15)="+Integer.toBinaryString(~10&15));
}
}
·static、final 对接口和类中变量、方法、继承特性的影响
static、final 和 接口
不可用 static 和 final 修饰接口;
不可用 static 和 final 修饰接口中的抽象方法;
接口中的变量默认是 public final static 的,即全局且不可更改。
static、final 和类
不可用static 修饰普通类,但是可以使用static修饰内部类
方法中不能使用static修饰变量——方法是栈封闭的,static是共享的属性
final 可以修饰类,被final修饰的类不可被继承
static修饰的变量将成为全局变量,所有该类的对象将共享;
final static 修饰的变量将成为全局变量且不可修改;
变量是不可被子类覆盖重写的,无论其是否被 static/final static 修饰;
被 static 修饰的方法将成为全局方法,被所有该类的对象享有,子类不可以再覆盖重写,但是可以拥有同名方法;
被 final 修饰的方法将不可被覆盖重写,并且不允许子类有同名方法。
子类不能覆盖重写父类成员反方和子类不能拥有父类的同名方法是两个概念。被static修饰的成员方法将不可以被子类覆盖重写,但是允许子类拥有自己的同名方法,即没有@Override。但是被final修饰的成员方法,子类既不能覆盖重写,也不允许拥有同名方法。
被final修饰的数组内容是可更改的哦,是不是被吓到了!
但是不可以被从新实例化,或者赋值为其他对象,因为final 的数组是引用地址不可更改,但是地址指向是内容是可变的。
·public 和 默认格式修饰的类/接口,public、protected、default、private 修饰的变量/常量、方法
·关于修饰类/接口
类分为内部类和外部类,class修饰的就是类,包含在类内部的类就是内部类
外部类仅可以使用public修饰或者无修饰(特指不能使用protected、default、private 修饰),外部类必须有唯一一个public修饰的类,该外部类与 Java 文件同名,可以跨包访问、被继承/被实现,在这个前提满足的情况下,这个类文件还可以有同级的不被public 修饰的class 且个数没有限制,仅限本包(package)继承和调用,不可跨包(package)调用或者继承
接口只能使用public修饰或者不修饰但是一个接口则可以全部没有public 修饰,一旦接口文件含被public修饰的接口,则接口文件必须与之同名。对于没有public 修饰的接口,仅限本包(package)继承,不可跨包(package)。接口文件内部也是可以包含多个接口的。
比如 a.java 内部 public interface a{} interface b{} interface c{}
内部类的修饰符可以是public、protected、private以及默认,但是非内部类只能是public 或者 默认修饰的
接口内部也可以包含内部类,是不是很神奇?而且默认且只能是public 修饰的。
·关于类中定义的常量/变量、方法(所谓子类是说继承或者实现了被修饰变量/常量、方法的类)
public 可在本类、本包其他类、其他包的类、本包子类、跨包子类访问
protected 可在本类、本包其他类、本包子类、跨包子类中访问,跨包其他类访问不可以。注意区分跨包和跨包子类。
default 可在本类、本包其他类、本包子类中访问,跨包子类、跨包其他类访问不可以。
private 仅本类可以访问。
总结:public和private是简单的,protected和default的唯一区别是,protected是可以别跨包继承的子类调用,但是跨包直接调用也是不可以的。default则是不允许跨包直接或者子类调用。强调一下,在类中常量/变量、方法对上面的规则同样适用。
·关于接口中的常量/变量、方法、本包子类、跨包子类的访问
由于接口的设计思想,导致接口的修饰没有类中那么复杂,可以认为全部都是可以跨包访问的。注意,接口是public的,然后需要子类实现该接口。对于没有public 修饰的接口,仅限本包(package)继承,不可跨包(package)
变量/常量:默认就是 public final static修饰的,即不可修改,且共享
方法:public $ abstract
而且,对于接口来说,其内部的方法和变量/常量默认情况下就是public的
·总概括:
类或接口只能是public或者无修饰,无修饰时仅限本包访问,然后是内部变量/常量、方法的规则
类或接口使用public 修饰时,访问域由内部变量/常量、方法的规则决定
所有的变量或者常量都无法被子类覆盖重写,但是可以拥有自己的同名变量,无论父类或者接口的变量是否被final修饰,究其根本是变量不参与Java的多态。
·枚举类可以别继承吗?
Java 类是单继承的,接口是多继承的。
一个类可以实现多个接口。
从JDK1.8开始,接口中可以写 default修饰的具体实现方法,无需子类覆盖重写。
枚举类是不可以被继承的,此外,枚举也无法继承其他类,但是枚举类可以实现其他接口。而且,枚举类型可以拥有普通变量和方法。
·String a="123"; 和 String a=new String("123");的区别?
equals 是 堆内容比较,==是栈内容比较
简单来说,String a="123";和String b="123";会共享一段内存,即只会在“123”首次声明时被创建,有一个 String 池的概念,此时 a.equals(b) 为 true;a==b 为true;
但是 String a=new String("123");String b=new String("123"); 会开辟两个内存,a.equals(b) 为 true,a==b 为false;
下面是存储位置说明:
1.单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中;
2.使用new String("")创建的对象会存储到heap中,是运行期新创建的;
3.使用只包含常量的字符串连接符如"aa" + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中;
4.使用包含变量的字符串连接符如"aa" + s1创建的对象是运行期才创建的,存储在heap中;
·String a=new String("123");创建了几个对象?
编译器,String pool 创建一个,即“123”;
运行期创建1个,在 heap 中 “123”;所以一共是两个。
String a="123",String b="123";一共创建几个对象?1个,因为这种情况下,Spring pool 会有一个“123”,a和b会共享。
String a="1"+"2"+"3";和String b="123";一共创建几个对象?1个,因为JVM会做优化,“1”+“2”+“3”和“123”是一样的。
但是String a="1";String b="2";+String c="3";,String d=a+b+c;会创建4个对象,如果String pool 中不存在 “1”,“2”,“3”,"123"
·异常分类:Error,Exception,RuntimeException以及其子类,其他异常
Error 和 受检异常 Exception 是并列关系
Exception 以及自定义的子类 受检异常必须try…catch,IDE 会提示,当你调用的方法有抛出这个异常时,要么 try...catch,要么 throws Exception 把异常继续向外传递
非受检异常 RuntimeException(为 Exception 的子类)以及自定的子类 一旦出现就表示程序出现了严重bug——应该尽力避免,非受检异常是受检异常的一个子类,IDE 不会强制捕获这些异常,但是你可以决定是否 try...catch,一般我们主张自定义的都是要 try...catch的
异常的分类
Error错误:JVM内部的严重问题,表示程序有致命的bug。一旦出现无法恢复,比如 OutOfMemoryError、StackOverFlowError
Exception异常:普通的问题。可以预知的异常,出现也属于正常,一般建议使用具体子类或者自定义子类,如IOException、SQLException、FileNotFoundException、ClassNotFoundException、自定义异常类等
RuntimeException及其子类:也叫非受检异常(unchecked exception).这类异常是编程人员的逻辑问题,一旦出现就表示出现bug。Java编译器不进行强制要求处理。 也就是说,这类异常再程序中,可以进行处理,也可以不处理。比如NullPointeException、IndexOutOfBoundsException、IllegalArgumentException、ArithmeticException
非RuntimeException:也叫受检异常(checked exception).这类异常是由一些外部的偶然因素所引起的。Java编译器强制要求处理。也就是说,程序必须进行对这类异常进行处理。
使用受检异常时的合法性要在编译时刻由编译器来检查。正因为如此,受检异常在使用的时候需要比非受检异常更多的代码来避免编译错误。
测试代码
public class RuntimeExceptionTest {
public void ma() throws RuntimeException{
System.out.println("RuntimeException 及其子类下的为非受检异常");
}
public void mb() throws IOException{
System.out.println("IOException 为受检异常");
}
public void mc() throws Exception{
System.out.println("Exception 为受检异常");
}
public static void main(String[] args) {
RuntimeExceptionTest r=new RuntimeExceptionTest();
//非受检异常可以不做处理
r.ma();
//受检异常则必须进行处理,使用try...catch或者再次抛出异常
try {
r.mb();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
r.mc();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
·Java 泛型-参数化类型
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
在Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
类型通配符上限通过形如Box<? extends Number>形式定义,相对应的,类型通配符下限为Box<? super Number>形式,其含义与类型通配符上限正好相反
泛型可以用于三种场景:1、简单的限定参数类型;2、用于类型转化的参数限定,即通配符类型上限和下限,因为在转化的时候,子类转父类是协转,是自动的,父类转子类是逆转,需要编写代码手动处理,即处理方式不同;3、结合Java的反射特性由父类获得子类的参数类型或者生成子类的实现,实现代码的集约。
·非受检异常的处理策略、
非受检异常是可以排除的,比如在IDE中,List会有警告提示,但是 写成 List<String> 即指定参数类型就不会警告了。
再一个对于无法排除非受检异常提示的,可以加注解@SuppressWarnings("unchecked"),即禁止警告非必检异常。
此外这个注解如果要用就使用在尽可能小的范围,比如变量级别。
·能用队列不用数组,决对不要在数组中包含队列
如果可以使用队列,就不要使用数组,因为涉及到类型转换的话,队列可以使用泛型是编译时检测,数组不可以直接和泛型结合,且是运行时检测。数组不可以直接和泛型结合,但是其子元素可以使用泛型,不过这样很不安全,要避免这样。
总结就是,尽量使用队列而不是数组,然后不要把队列和数组混合使用,避免用法:List<String>[] List=new List<String> [1]
·关于静态工厂方法
工厂方法是产生其他类新对象的,静态工厂方法可以通过 类名.方法名 获取新对象。实例工厂方法需要先实例化工厂类,然后方法名,获取新对象。
效果和使用其他类的构造方法是一样的
·关于校验规则
整形类型不能为空
@NotNull(message="整形类型 不能为空")
字符串不可为空-表示入参必须有这个名字的参数
size是有这个参数了,但是限定字符串的长度
@NotBlank(message="字符串类型参数必须存在,空串算作空,也是不合法的,除非用 NotEmpty")
@Size(min=0,max=10,message="字符串长度需要在0-10之间,这个0只有参数存在时才有效")
SOLID
单一职责:指的是一个类或者方法只做一件事。如果一个类承担的职责过多,就等于把这些职责耦合在一起,
一个职责的变化就可能抑制或者削弱这个类完成其他职责的能力。
开闭原则:对扩展开放,对修改关闭。意为一个类独立之后就不应该去修改它,而是以扩展的方式适应新需求。
里氏替换:所有基类出现的地方都可以用派生类替换而不会使程序产生错误。子类可以扩展父类的功能,但是不能改变父类原有的功能。
接口隔离:类不应该依赖不需要的接口,知道越少越好。
依赖反转:指的是高级模块不应该依赖低级模块,而是依赖抽象。抽象不能依赖细节,细节要依赖抽象。
main 方法可以被自类重写或者当做普通方法调用
> 当然,需要注意的是,main 如果 加static 就需要按照静态方法的规则,否则就是普通规则
import org.junit.Test;
public class DelA {
private static final int COUNT_BITS = Integer.SIZE - 3;
@Test
public void test(){
System.out.println(COUNT_BITS);
}
public static void main(String[] args) {
System.out.println("你好");
}
@Test
public static void main() {
main(null);
}
private static class B extends DelA{
public static void main(String[] args) {
main();
System.out.println("你好2");
}
}
}
运行 classB main 方法结果
你好
你好2