内部类
什么是内部类
概念
在一个类的内部再顶一个完整的类。
特点
-
编译之后生成独立的字节码文件,会有$隔开。
-
内部类可直接访问外部类的私有成员,而不破坏封装。
-
可为外部类提供必要的内部共能组件。
成员内部类
-
在类内定义,与实例变量方法同级别。
-
外部类的一个实例部分,创建内部类对象时,需要依赖外部类对象。
两种调用方法: //第一种 Outer outer = new Outer(); Inter inter = outer.new Inter(); inter.show(); //第二种 Inter inter = new Outer().new Inter(); inter.show();
-
当外部类,内部类存在重名属性时,在内部类访问时会优先访问内部类属性。
//想要在内部类中访问外部类相同属性名,可以在内部类中这样用。 System.out.println(Outer.this.name);
-
在内部类中,是不能设置静态成员,但是可以加上静态常量,加上final修饰符就可以了。
静态内部类
-
使用static修饰
-
可以添加任意访问修饰符(public,protected,默认,private)
-
不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。
-
在静态内部类中,可以访问外部类的静态成员,但是如果想要访问外部类的非静态属性,需要在静态内部类中创建一个外部类,静态成员是通过类名来访问的。
Outer.Innter inter = new Outer.Inner(); inner.show(); Outer.Inner.show(); System.out.println(Outer.Inner.count); //count是静态的
-
只有内部类才能被修饰为静态的。
局部内部类
- 定义在外部类方法中,作用范围和创建对象范围仅限于当前方法。
- 局部内部类不能加任何访问修饰符。
- 可以直接访问外部类属性,如果这个方法被修饰成静态的,那就不能直接访问外部类属性,需要创建对象。
- 想要调用局部内部类中的成员,必须在存放局部内部类这个方法中,去创建局部内部类的对象,通过这个对象进行访问。
- 在局部内部类中想要访问,它所处的方法中的局部变量,这个局部变量必须是常量,在jdk1.8以后就不用使用final,因为在调用的时候,就会直接转换成常量。
匿名内部类
//接口
package Third.Class;
interface Usb {
void service();
}
//匿名内部类
package Third.Class;
public class TestUsb {
/**@ time 2021-03-27 15:23 **/
public static void main(String[] args) {
// 创建接口类型的变量
/*
Usb usb = new Mouse(); //类似于多态
usb.service();
*/
//局部内部类
/*
class keyboard implements Usb{
@Override
public void service() {
System.out.println("连接电脑成功,键盘开始工作了");
}
}
Usb usb = new keyboard();
usb.service();
*/
//基于接口的匿名内部类
//上面那个局部内类就是用一次,就不用了,浪费了,使用匿名内部类进行优化
//编译类型(栈):Usb接口
//运行类型(堆):匿名内部类(相当于创建一个局部内部类)
Usb usb = new Usb() {
@Override
public void service() {
System.out.println("我是匿名内部类实现的");
}
};
usb.service();
//基于(抽象)类的匿名内部类
//编译类型(栈):Father
//运行类型(堆):匿名内部类
//也可以作为参数进行传递
Father father = new Father("sxy"){
//这也可以重写父类的方法
};
}
}
class Father{
public Father(String name){
}
}
Object类
- 所有类的直接或间接的父类。
getClass()方法
-
public final Class<?> getClass(){}
-
返回引用中存储的实际对象类型。
-
应用:通常用于判断两个引用中实际存储对象类型是否一致。
Student s1 = new Student(); Student s2 = new Student(); Class class1 = s1.getClass(); Class class2 = s2.getClass(); if(class1==class2) System.out.println("Yes"); else System.out.println("No");
hashCode()方法
-
public int hashCode(){}
-
返回该对像的哈希码值(对象的地址)。
-
一般情况下相同对象返回相同哈希码。
Student s3 = s1; System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); System.out.println(s3.hashCode()); //s1与s2地址不一样,s1和s3地址是一样的
toString()方法
-
public String toString(){}
-
返回该对象的字符串表示(表现形式)
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
可以重写这个方法。(可以Ctrl+INS快捷操作)
public String toString(){ return name+":"+age; //自己定义一个String类型的返回值 }
equals()方法
-
public boolean equals(Object obj){}
public boolean equals(Object obj){ return (this==obj); } System.out.println(s1.equals(s2));
-
默认实现为(this==obj),比较两个对象地址是否相同。
-
可进行重写,比较两个对象的内容是否相同。
//更详细的equals和hashCode方法的重写https://www.cnblogs.com/yuxiaole/p/9570850.html public boolean equals(Object obj){ if(this==obj){ return true; } if(obj==null){ return false; } /* if(this.getClass()==obj.getClass()){} //一般不这样用 */ if(obj instanceof Student){ //判断obj是不是Student的实例 Student s = (Student)obj; //obj传进来时是Object if(this.name.equals(s.getName())&&this.age==s.getAge()){ //条件根据具体来设置 return true; } } return false; }
finalize()方法
-
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。
-
垃圾对象:没有有效引用指向此对象时,为垃圾对象。
-
垃圾回收:由GC销毁垃圾对象,释放数据存储空间。
-
自动回收机制:JVM的内存耗尽,一次性回收所有的垃圾对象。
-
手动回收机制:使用System.gc();通知JVM执行垃圾回收。
//类似这样的代码就是垃圾对象 new = Student("1",2); new = Student("2",3); protected void finalize() throws Throwable { } //jdk源码 //重写 protected void finalize() throws Throwable { System.out.println(“垃圾正在回收”); } //在测试时这样做 System.gc(); //系统就会自动调用finalize()进行回收垃圾
包装类
什么是包装类
-
基本数据类型所对应的引用数据类型。 (基本数据类型都是放在栈里,速度快,而包装类对象的实体是存放在堆里,对象的引用存放在栈里)
-
Object可统一所有数据,包装类的默认值是null。
boolean Boolean byte Byte char Character short Short int Integer long Long float Float double Double
类型转化与装箱,拆箱
//装箱:基本类型转成引用类型
//基本类型
int num1 = 10; //放在栈里的
//引用类型
Integer integer1 = new Integer(num1);
Integer integer2 = Integer.valueOf(num1);
//拆箱:引用类型转成基本类型
Integer integer3 = new Integer(100); //此时这个100是放在堆里
int num2 = integer3.intValue();
//以上方式是jdk1.5之前装箱拆箱的操作,jdk1.5之后java就提供了自动装箱和拆箱,而自动装箱拆箱就是把一些jdk1.5之前的步骤隐藏了
int age1 = 30;
//自动装箱
Integer integer4 = age; //通过反编译是这样:Integer integer4 = Integer.valueOf(age);
//自动拆箱
int age2 = integer4; //类比上面
基本类型和字符串之间转换
//1基本类型转成字符串
int n1 = 15;
String s1 = n1+""; //第一种
String s2 = Integer.toString(n1); //第二种
//String s2 = Integer.toString(n1,16);//16是16进制表示,这里可以写其他进制,例如17,18等等也可以写。
//2字符串转成基本类型
String st1 = "150";
int n2 = Integer.parseInt(st1); //st不能是非数字字符
//boolean字符串形式转成基本类型
String st2 = "true"; //只有字符"true"会转成数字true,其他的字符都会转成数字false
boolean b1 = Boolean.parseBoolean(str2);
Integer缓冲区
public class Test {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a==b); // 打印true
System.out.println(a==b); // 打印false
}
}
//int IntegerCache缓存中存在着[-127,128]范围内的数就可以直接数字的封装类,但是超过这个范围就要重新new一个出来。
//Integer自动装箱的源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//自动装箱和非自动装箱有什么区别?
Integer i = new Integer(1)和Integer i = 1;
1,第一种方式不会触发自动装箱的过程;而第二种方式会触发
2,在执行效率上和资源占用上的区别。第二种在一般情况下会优于第一种,但是不是绝对的,因为有可能使用的是相同的对象。
//double 处理缓存区比较困难,所以就没有,就是直接new
//源码
public static Double valueOf(double d){
return new Double;
}
String类
字符串的存储
-
字符串是常量,创建之后不可改变。
-
字符串字面值存储在字符串池中,可以共享。
String s1 = "hello"; s1 = "world"; //此时不是修改了"hello",而是s1指向了"world" String s2 = "world"; //s1和s2此时指向的是常量池中同一地方 System.out.println(s1 == s2); //true String s3 = new String("java"); //这样的方式,一次创建两个对象。 /*上面直接赋值型的方式,字符串对象创建在常量池中,在栈中存放着指向常量池对象的地址;而用new这种方式创建的字符串,它也会在常量池中创建一个字符串对象(如果常量池有就不会创建),但是在栈中存放的不是指向常量池对象的指针,而是在堆中创建一个对象,指向堆中的对象*/ String s4 = new String("java"); //s3不等于s4,因为他们指向堆中不同的对象,但是在堆中不同的对象引用的是同一个常量池的字符串对象。 //所以判断字符串是否相等,需要用equals System.out.println(s3.equals(s4));//false
常用方法
public int length(); //返回字符串的长度
public char charAt(int index); //根据下标获取字符
public boolean contains(String str); //判断当前字符串是否包含str
public char[] toCharArray(); //将字符串转换成数组
public int indexOf(String str); //查找str首次出现的下标,找不到返回-1
public int indexOf(String str,int index); //从index开始判断
public int lastIndexOf(String str); //查找str最后一次出现的下标
public String trim(); //去掉字符串前后的空格
public String toUpperCase(); //将小写转成大写
public String toLowerCase(); //将大写转成小写
public boolean endWith(String str); //判断字符串是否是以str结尾
public boolean startWith(String str); //判断字符串是否以str开始
public String replace(char oldChar,char newChar); //将旧字符或字符串替换成新的字符或字符串
public String[] split(String str); //根据str做拆分
String st = "java is the best and,c++ also";
String[] arr1 = st.split(" "); //这样是以空格分隔
String[] arr2 = st.split("[ ,]"); //这样就可以空格和逗号一起分隔
String[] arr2 = st.split("[ ,]+"); //如果出现两个空格在一起,就可以这样操作
public boolean equals(String str); //比较字符串值是否相等
public boolean equalsIgnoreCase(String str); //忽略大小写的比较
public int compareTo(String str); //返回一个差值
public String substring(int beginIndex); //从beginIndex截取到结尾
public String substring(int beginIndex,int endIndex); //从beginIndex截取到endIndex
StringBuffer&StringBuider
-
可变长字符串
-
优点比String效率高,更节省内存
public class Application { public static void main(String[] args) { //StringBuilder和StringBuffer用法基本是一样的 //区别StringBuilder比StringBuffer效率高一点,但是线程没有StringBuffer高 //StringBuffer sb = new StringBuffer(); StringBuilder sb = new StringBuilder(); //1.append();字符串追加函数 sb.append("sxy "); System.out.println(sb.toString()); sb.append("love "); System.out.println(sb.toString()); sb.append("syj"); System.out.println(sb.toString()); //2.insert();添加 sb.insert(0,"5211314 "); //insert(index,str);index是下标 System.out.println(sb.toString()); //3.replace();替换 sb.replace(0,8,"我爱你一生一世 "); //end(最后元素的下一个) System.out.println(sb.toString()); //delete();删除 sb.delete(0,8); //sb.delete(0,sb.length());清空 System.out.println(sb.toString()); //reverse();翻转 sb.reverse(); System.out.println(sb.toString()); } } /* sxy sxy love sxy love syj 5211314 sxy love syj 我爱你一生一世 sxy love syj sxy love syj jys evol yxs */
//验证StringBuilder和String的效率 public class Application { public static void main(String[] args) { long start = System.currentTimeMillis(); String st = ""; for(int i = 1; i <= 99999; i++){ st += i; } //StringBuilder st = new StringBuilder(); //for(int i = 1; i <= 99999; i++)st.append(i); long end = System.currentTimeMillis(); //前者时间2371后者时间8,差距非常大 System.out.println(end-start); } }
BigDecimal
public class Application { public static void main(String[] args) { double d1 = 1.0; double d2 = 0.8; System.out.println(d1-d2); //输出的是0.0999999999999998 //原因:double的存储是存它的近似值,所以在计算的时候存在误差 //BigDecimal高精度浮点数计算 BigDecimal b1 = new BigDecimal("1.0"); BigDecimal b2 = new BigDecimal("0.9"); //减法 BigDecimal b3 = b1.subtract(b2); System.out.println(b3); //0.1 //加法 BigDecimal b4 = b1.add(b2); System.out.println(b4); //乘法 BigDecimal b5 = b1.multiply(b2); System.out.println(b5); //除法 BigDecimal b6 = b1.divide(b2,10,ROUND_HALF_DOWN); //如果出现无限循环小数,必须要表留一些位数才不会报错 //BigDecimal b7 = b1.divide(b2);普通的 System.out.println(b6); } }
Date
public class Application {
public static void main(String[] args) {
//1.创建Date对象
//今天
Date date1 = new Date();
System.out.println(date1.toString());
System.out.println(date1.toLocaleString()); //有删除线说明过时了
//昨天
Date date2 = new Date(date1.getTime()-3600*24*1000);
System.out.println(date2.toLocaleString());
//2.after,before
System.out.println(date1.after(date2)); //判断date1是否在date2后面
System.out.println(date1.before(date2));//判断date1是否在date2前面
//3.比较compareTo();比较毫秒值
System.out.println(date1.compareTo(date2));
//4.equals();
System.out.println(date1.equals(date2));
}
}
Calendar
public class Application {
public static void main(String[] args) {
//1.创建Calendar对象
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime().toLocaleString());
//System.out.println(calendar.getTimeInMillis());//获得秒
//2.获取时间信息
//获取年
int year = calendar.get(calendar.YEAR);
//获取月 返回时是从0-11
int month = calendar.get(calendar.MONTH)+1;
//获取日
int day = calendar.get(calendar.DAY_OF_MONTH);
//获取时
int hour = calendar.get(calendar.HOUR_OF_DAY);
//获取分
int minute = calendar.get(calendar.MINUTE);
//获取秒
int second = calendar.get(calendar.SECOND);
System.out.println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second);
//3.修改时间
Calendar calendar2 = Calendar.getInstance();
calendar2.set(Calendar.DAY_OF_MONTH,5); //把时间改成第5日
System.out.println(calendar2.getTime().toLocaleString());
//4.add方法修改时间
calendar2.add(Calendar.HOUR,1); //加一个小时
System.out.println(calendar2.getTime().toLocaleString());
//5.获得某个月的最大/小日数
calendar2.add(Calendar.MONTH,1);
int max = calendar2.getActualMaximum(Calendar.DAY_OF_MONTH);
int min = calendar2.getActualMinimum(Calendar.DAY_OF_MONTH);
System.out.println("max:"+max+" min:"+min);
}
}
/*
2021年4月8日 下午6:40:20
2021/4/8 18:40:20
2021年4月5日 下午6:40:20
2021年4月5日 下午7:40:20
max:31 min:1
*/
SimpleDateFormat
public class Application {
public static void main(String[] args) throws Exception{
//1.创建对象:y年M月d日H时m分s秒--特殊字符
//SimpleDateFormat sp = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
SimpleDateFormat sp = new SimpleDateFormat("yyyy/MM/dd");
//2.创建Date
Date date = new Date();
//格式化date(把日期转成字符串)
String st = sp.format(date);
System.out.println(st);
//解析(把字符串转成时间)
Date date1 = sp.parse("1999/11/26"); //要符合上面的格式,要抛出一个异常
System.out.println(date1);
}
}
/*
2021/04/08
Fri Nov 26 00:00:00 CST 1999
*/
System类
public class Application {
public static void main(String[] args){
//1.arraycopy;数组的复制
//System.arraycopy(源数组,从哪个位置开始复制0,目标数组,目标数组的位置,复制长度);
int[] arr = {10,11,12,13,14,15,16,17};
int[] dest = new int[8];
System.arraycopy(arr,0,dest,0,8);
for (int i : dest) System.out.print(i+" ");
//Arrays.copyOf();这个复制方法在源码里还是调用了System.arraycopy();
System.out.println();
//2.currentTimeMillis();从某个时间到现在一共过去多少秒,可以计时计算代码的执行时间
//System.out.println(System.currentTimeMillis());
long start = System.currentTimeMillis();
for(int i = 0; i <= 100000; i++){
for(int j = 0; j <= 100000; j++){
for(int k = 0; k <= 100000; k++){
int s = i+j+k;
}
}
}
long end = System.currentTimeMillis();
System.out.println(end-start);
//3.gc();垃圾回收在上面Object类中有讲解
//4.退出JVM
System.exit(0);
}
}
/*
10 11 12 13 14 15 16 17
2551
*/