一、内部类
在一个类的内部再定义完整的类。具有1)编译后可以产生独立的字节码文件。2)内部类可以直接访问外部类的私有成员而不破坏封装的特点。
1、成员内部类
在类的内部定义,与实例变量和实例方法同级别。成员内部类是外部类的一个实例部分,创建内部类对象时,必须依赖外部类被对象,即要先创建外部类对象再创建内部类对象。当外部类和内部类存在重名属性时,会优先访问内部类属性,如要访问外部类的重名属性则使用Outer.this。成员内部类不能定义静态成员,但可以定义静态常量static final。
public class Outer {
//实例变量
private String name = "张三";
private int age = 20;
class Inner{
private String address = "福建";
private String phone = "110";
//属性和外部类的属性名字相同 Outer.this
private String name = "李四";
public void show(){
//打印外部类的属性
System.out.println(Outer.this.name);
System.out.println(Outer.this.age);
//打印内部类的属性
System.out.println(address);
System.out.println(phone);
System.out.println(name);//李四
}
}
public class TestOuter {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.show();
}
}
2、静态内部类
不依赖外部对象,可以直接创建或通过类名访问,可以声明静态成员。例子中之所以先创建外部类再创建内部类是因为这是另一个Class文件,需要先创建外部类对象。
public class Outer {
private String name = "张三";
private int age = 18;
static class Inner{
private String address = "上海";
private String phone = "111";
//静态成员变量
private static int count = 1000;
public void show(){
//调用外部类的属性
Outer outer = new Outer();
System.out.println(outer.age);
System.out.println(outer.name);
//调用静态内部类的属性和方法
System.out.println(address);
System.out.println(phone);
//调用静态内部类的静态属性
System.out.println(Inner.count);
}
public static void main(String[] args) {
Inner inner = new Inner();
System.out.println(new Inner().address);
}
}
public class TestOuter {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.show();
}
}
3、局部内部类
定义在外部类的方法中,使用范围、作用范围和创建对象范围仅限于当前方法。局部内部类访问外部类当前方法中的局部变量时,因无法保证变量的声明周期与自身相同,变量必须修饰final。
public class Outer {
private String name = "刘德华";
private int age = 18;
public void show(){
final String address = "上海";
class Inner{
//局部内部类的属性
private String phone = "111";
private String email = "xxxxxxx.com";
public void show2(){
//访问外部类的属性
System.out.println(name);//省略了Outer.this.name
System.out.println(age);//如果方法show是静态的就不能直接访问,需要创建外部对象
//访问内部类的属性
System.out.println(this.phone);
System.out.println(email);//省略了this
//访问局部变量,jdk1.7要求:变量必须是常量final,jdk1.8后自动添加final
System.out.println(address);
//这里已经不是调用address了而是System.out.println("上海");address消失了
}
}
//方法并没有调用局部内部类,所以要创建局部内部类对象
Inner inner = new Inner();
inner.show2();
}
public class TestOuter {
public static void main(String[] args) {
Outer outer = new Outer();
outer.show();
}
4、匿名内部类
没有类名的局部内部类(其实编译class文件可以看到有系统自动生成的名字的)一切特征都与局部内部类相同。匿名内部类必须继承一个父类或实现一个接口。其实匿名内部类就是定义类、实现类和创建对象的语法合并,要注意只能创建一个该类的对象!!!
public interface Usb {
void seivice();
}
public static void main(String[] args) {
Usb usb = new Usb(){
@Override
public void seivice() {
System.out.println("连接电脑成功");
}
};
usb.seivice();
}
二、Object类
Object类是超类、基类、是所有类的直接或间接父类,位于继承树的最顶层。任何类如果没有书写extends显示继承某个类,都默认直接继承Object类,如果有则为间接类。Object类中所定义的方法是所有对象都具有的方法。可以用Object存储任何对象,作为参数可以接受任何对象,作为返回值则可以返回任何对象。
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age){
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
1)equals
对于基本数据类型来说,没有equals方法;对于引用类型来说,equals比较的可能是内存地址,也可能是内存地址和值都比较,从equals的实现上来说,其实用的比较也是==,所以equals的具体比较得看重写后的方法。对于Object类型来说,equals方法比较的是两者的内存地址。但很多类型都重写了equals方法,例如常用的String类型比较的就是内存地址和值。==比较基本类型时比较的是值,比较引用类型时比较的是地址。
public class Equals {
public static void main(String[] args) {
Student s1 = new Student("aaa",20);
Student s2 = new Student("bbb",22);
System.out.println(s1.equals(s2));//没重写前是false 比的是地址
Student s3 = new Student("小明",18);
Student s4 = new Student("小明",18);
System.out.println(s3.equals(s4));//重写后比较内容
}
}
equals也可以进行覆盖,覆盖步骤为1)比较两个引用是否指向同一个对象。2)比较obj是否为null。3)判断两个指向的实际对象类型是否一致。4)强制类型转换。5)依次比较各个属性值是否相同。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
2)getClass
getClass()返回引用中存储的实际对象类型,通常用于判断两个引用中实际存储对象类型是否一致。
public static void main(String[] args) {
Student s1 = new Student("aaa",20);
Student s2 = new Student("bbb",22);
//判断s1和s2是不是同一个类型
Class class1 = s1.getClass();
Class class2 = s2.getClass();
if (class1 == class2){
System.out.println("s1和s2属于同一个类型");
}else {
System.out.println("s1和s2不属于同一个类型");
}
}
3)finalize
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象,进入回收队列。垃圾对象是指没有有效引用指向此对象的对象。垃圾回收指由GC销毁垃圾对象,释放数据存储空间。这里回收有两种机制,分别是自动回收机制:JVM的内存耗尽,一次性回收所有的垃圾对象;另一种是手动回收机制:使用System.gc();通知JVM进行垃圾回收。
4)hashCode
用来返回该对象的哈希码值。哈希值根据对象的地址或字符串或数字使用hash算法计算出来的int类型的数值。一般情况下相同对象返回相同的哈希码。这里s1和s2哈希码不同不是因为内容不同,是因为两个不为同一个对象。
Student s1 = new Student("aaa", 22);
Student s2 = new Student("bbb", 21);
System.out.println(s1.hashCode());//2986934
System.out.println(s2.hashCode());//3017716
Student s3 = s1;//把s1的地址给s3
System.out.println(s3.hashCode());//2986934
5)toString
返回该对象的字符串表示。返回的对象为类时,返回的是位置@16进制哈希值。toString()可以根据不同需求覆盖方法,如下展示对象的各个属性值。
public static void main(String[] args) {
Student s1 = new Student("aaa",20);
Student s2 = new Student("bbb",22);
System.out.println(s1.toString());//com.lhp.Class.Object.Student@79032d84
System.out.println(s2.toString());//位置@哈希值(16进制)
}
三、包装类
基本数据类型所对应的引用数据类型,默认值是null。装箱是基本类型转换引用类型,eg:在栈中定义了一个10,放入堆中引用。拆箱是引用类型转换成基本类型。jdk1.5后提供自动装箱和拆箱,class文件反编译后还是装箱和拆箱。
int num1 = 10;
//使用Integer类创建对象
//装箱
Integer integer1 = new Integer(num1);
Integer integer2 = Integer.valueOf(num1);
System.out.println(integer1);
System.out.println(integer2);
//拆箱,引用类型转换成基本类型
Integer integer3 = new Integer(100);
int num2 = integer3.intValue();
System.out.println(num2);
int age = 30;
//自动装箱
Integer integer4 = age;
System.out.println(integer4);
//自动拆箱
int age2 = integer4;
System.out.println(age2);
数字使用Integer类创建对象,基本类型转换成字符串有两种方法,一是直接加空字符串,二是使用Integer的toString方法;字符串转换成基本类型使用parsexxx();Boolean字符串形式转换成基本类型true转换成true,其他的都是false。
//基本类型转成字符串
int n1 = 15;
String s1 = n1 + "";//+空字符串
String s2 = Integer.toString(n1);//Integer中的toString方法
String s3 = Integer.toString(n1,16);//16进制
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
//字符串转成基本类型
String str = "150";
int n2 = Integer.parseInt(str);
System.out.println(n2);
String str2 = "true";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
Java预先创建了256个常用的整数包装类型对象称为整数缓冲区,可以直接进行复用。即从堆中的整数缓冲区里把已经存在的数字的地址放入栈中,范围是-127~127,自动装箱时使用Integer.valueOf(),如果不在整数缓冲区内的话使用new Integer()。
Integer integer5= new Integer(100);
Integer integer6= new Integer(100);
System.out.println(integer5 == integer6);//false引用对象 比较的是地址
Integer integer7 = 100;
Integer integer8 = 100;
System.out.println(integer7 == integer8);//true
Integer integer9 = 200;
Integer integer10 = 200;
System.out.println(integer9 == integer10);//false
四、String类
1)字符串是常量,创建后不可改变,字符串字面值存储在字符串池中,可以共享,字符串串池在方法区中,JDK不同版本字符串池不一定字方法区里,7.0后被移到了堆中。如果是直接赋值的话是产生一个对象,字符串池中储存;如果是new一个赋值的对象的话是产生两个对象,在堆和池中各一个,比较浪费空间。重新给原来有值的字符串变量赋值时,不是直接修改数据,而是重新在方法区中开辟了一个空间放入字面值,原来的常量就成了垃圾。
String name = "hello";//”hello“常量存储在字符串池中,
name = "zhangsan";//"zhangsan"赋值给name变量,原来的hello成为了垃圾
String name2 = "zhangsan";
String str = new String("java");
String str2 = new String("java");
System.out.println(str == str2);//false
System.out.println(str.equals(str2));//true
String content = "java 是最好的java编程语言,java";
length();返回字符串长度。
System.out.println(content.length());//22
charAt(int index);根据下标获取字符串。
System.out.println(content.charAt(0));//j
System.out.println(content.charAt(content.length()-1));//a
contains(String str);判断当前字符串中是否含有str。
System.out.println(content.contains("java"));
System.out.println(content.contains("python"));
toCharArray();将字符串转换成数组。
System.out.println(Arrays.toString(content.toCharArray()));
//[j, a, v, a, , 是, 最, 好, 的, j, a, v, a, 编, 程, 语, 言, ,, j, a, v, a]
indexOf(String str);查找str首次出现的下标,存在则返回下标,不存在则返回-1。
System.out.println(content.indexOf("java"));//0
System.out.println(content.indexOf("java",4));//9 从4下标开始找
lastIndexOf(String str);查找字符串在当前字符串中最后一次出现的下标索引。
System.out.println(content.lastIndexOf("java"));//18
trim();去掉字符串前后的空格。
String content2 = " Hello world ";
System.out.println(content2.trim());
toUpperCase();将小写转换成大写。
System.out.println(content2.toUpperCase());
System.out.println(content2.toLowerCase());
endWith(String str);判断字符串是否以str结尾。
String filename = "hello.java";
System.out.println(filename.endsWith("java"));
System.out.println(filename.startsWith("hello"));
replace(Char oldChar,Char newChar);把旧字符替换成新字符。
System.out.println(content.replace("java", "python"));
split(String str);根据str进行拆分。
String say = "java is the best programing language,xiang";
String[] arr = say.split("[ ,]+");//用空格和逗号,且多个逗号和空格算一个
System.out.println(Arrays.toString(arr));
System.out.println(arr.length);//7
for (String string : arr){
System.out.print(string+" ");
}//java is the best programing language xiang false
compareTo();根据字典表里的顺序比较大小。从第一个字符开始比,如果相同则下一个字符比较,不同则相减。如果被比较值的长度比比较值短,且没比出结果则比较长度。
String s1 = "hello";
String s2 = "HELLO";
System.out.println(s1.equals(s2));//false
System.out.println(s1.equalsIgnoreCase(s2));//忽略大小写比较true
String s3 = "abc";//97
String s4 = "xyz";//123
System.out.println(s3.compareTo(s4));//-26
String s5 = "abc";
String s6 = "abcxzy";
System.out.println(s5.compareTo(s6));//-3 这里因为abc后面没有东西比了 所以比长度
2)可变长字符串
可变长字符串有两种使用方法,分别是StringBuffer和StringBuilder。StringBuffer:可变长字符串,由JDK1.0提供,运行效率比StingBuilder慢,线程安全。StringBuilder:可变长字符串,JDK5.0提供,线程不安全,常用于单线程。可变长字符串比String效率高,更省内存。
long start = System.currentTimeMillis();
String string="";
for (int i = 0;i < 99999;i++){
string+=i;
}
System.out.println(string);
long end = System.currentTimeMillis();
System.out.println("用时:" + (end - start));//8992
long start2 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0;i < 99999;i++){
sb.append(i);
}
System.out.println(sb.toString());
long end2 = System.currentTimeMillis();
System.out.println("用时:" + (end2 - start2));//9
常用append();insert();replace();delete();来进行追加、添加、替换和删除。
StringBuilder sb = new StringBuilder();
sb.append("java number one");
System.out.println(sb.toString());
sb.append(" java good");
System.out.println(sb.toString());
sb.insert(0,"我在最前面");
System.out.println(sb.toString());
sb.replace(0,5,"Hello,");
System.out.println(sb.toString());
sb.delete(0,5);
System.out.println(sb.toString());
//清空
sb.delete(0,sb.length());
System.out.println(sb.toString());
五、BigDecimal
因为浮点数的采用近似计算,在一些需要精确计算的时候就会产生精度缺失,所以一般使用BigDecimal来精确计算浮点数。BigDecimal使用add(new BigDecimal);subtract(new BigDecimal);multiply(new BigDecimal);divide(new BigDecimal);来进行加减乘除。注意除法如果除不尽的话会报错,可以采用四舍五入来避免报错。
BigDecimal bd1 = new BigDecimal("1.0");//一定要为字符串,不然还是可能精度缺失
BigDecimal bd2 = new BigDecimal("0.9");
BigDecimal r1 = bd1.subtract(bd2);//减法
System.out.println(r1);
BigDecimal r2 = bd1.add(bd2);//加法
System.out.println(r2);
BigDecimal r3 = bd1.multiply(bd2);//乘法
System.out.println(r3);
BigDecimal r4 = new BigDecimal("1.4").subtract(new BigDecimal(0.5)).divide(new BigDecimal("0.9"));
//(1.4 - 0.5)/0.9
System.out.println(r4);
BigDecimal r5 = new BigDecimal("10").divide(new BigDecimal("3"),2,BigDecimal.ROUND_HALF_UP );
//10/3 这里保留两位小数,四舍五入
System.out.println(r5);
六、Date
Date表示特定的瞬间,精确到毫秒。Date类中的大部分方法都已经被Calender类中的方法所取代。
1秒 = 1000毫秒 1毫秒 = 1000微秒 1微秒 = 1000纳秒
//今天
Date date1 = new Date();
System.out.println(date1.toString());//Fri Feb 11 15:03:36 CST 2022
System.out.println(date1.toLocaleString());//2022-2-11 15:03:36
//昨天
Date date2 = new Date(date1.getTime() - 60*60*24*1000);//精确到毫秒
System.out.println(date2.toLocaleString());//2022-2-10 15:03:36
boolean b1 = date1.after(date2);//今天在昨天的后面
System.out.println(b1);//true
boolean b2 = date1.before(date2);//今天在昨天的前面
System.out.println(b2);//false
//compareTo();比较的是毫秒值 返回1,0,-1 代表正负数和0
int d = date1.compareTo(date2);
System.out.println(d);
date1.equals(date2);//flase
七、Calender
Calender常用于日历,是对Date的改善。注意创建Calender对象时一般用Calendar calendar = Calendar.getInstance();静态方法返回子类对象,而不是new。Calender使用getTime();来返回时间。用get();来获取需要的时间信息,要注意因为国外的月份的记录方法习惯不同,所以获取月份时是从0~11。修改时间一般用set和add来修改时间。可以用getActualMaximum();来获取最大值。
//创建Calendar对象
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime().toLocaleString());
System.out.println(calendar.getTimeInMillis());
//获取时间信息
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);//Date
int hour = calendar.get(Calendar.HOUR_OF_DAY);//HOUR12小时,HOUR_OF_DAY24小时
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
System.out.println(year+"年"+(month+1)+"月"+day+"日"+hour+":"+minute+":"+second);
//修改时间
Calendar calendar2 = Calendar.getInstance();
calendar2.set(Calendar.DAY_OF_MONTH,5);
System.out.println(calendar2.getTime().toLocaleString());
calendar2.add(Calendar.HOUR,-1);//减一小时
System.out.println(calendar2.getTime().toLocaleString());
int max = calendar2.getActualMaximum(Calendar.DAY_OF_MONTH);
int min = calendar2.getActualMinimum(Calendar.DAY_OF_MONTH);
System.out.println(max);
System.out.println(min);
八、SimpleDateFormat
SimpleDateFormat一般用来格式化时间(日期 —> 文本)或解析(文本 —> 日期)。格式化用format();解析用parse();格式化时年用y,月用M,日用d,小时用H(0~23)h(1~12),分钟用m,秒用s,毫秒用S。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date date = new Date();
//格式化date
String str = sdf.format(date);
System.out.println(str);
//解析
Date date2 = sdf.parse("1999年11月22日 14:15:55");
System.out.println(date2);
九、System
System构造方法是私有的,不要要创建对象使用。常用的有arraycopy();数组的复制,这里src表示源数组,srcPos表示从哪个位置开始复制,dest表示目标数组,length表示复制的长度。currenTimeMillis();获取毫秒数。gc();告诉垃圾回收期回收。exit(int status);退出JVM,推出后的语句均不执行。
int[] arr = {20,19,29,4,55,90,1,2};
int[] dest = new int[8];
System.arraycopy(arr,4,dest,4,4);
for (int i = 0; i < dest.length; i++) {
System.out.println(dest[i]);
}
//Array.copyOf(original,newLength)其实也是用System.arraycopy实现
System.out.println(System.currentTimeMillis());
long start = System.currentTimeMillis();
for (int i = 0;i < 99999;i++){
int result =+i;
}
long end = System.currentTimeMillis();
System.out.println("用时:" + (end - start));
System.gc();
System.exit(0);
System.out.println("hello");