1.基本类型
1.八大基本类型分为四大数据类型
1.整型
byte short int long
2.浮点型
float double
3.字符型
char
4.布尔类型
boolean
2.八大基本类型的包装类
1.byte和包装类Byte
一个字节有8位,数据存储是以字节为单位的,每8个bit为一字节,所以一个byte占用8位
2. short和包装类Short
short占用16位,数据范围是-32768-32767,所以最大的存储是65536
3.int和包装类Integer
int类型占用32位,数据存储范围是-2^ 31-(2^31)-1,最大存储量是(2^32)-1
4.double和包装类Double
dubble大小占用64位,取值范围是4.9e-324~1.8e308
5.boolean和包装类Bollean
布尔类型只有true和false两个值
6.char和包装类Character
字符char占用 6位,使用时用单引号赋值。char a='S'
7.float和包装类Float
占32位,数据范围在3.4e-45~1.4e38,在使用赋值时必须在数字后加上f或F
8.long和包装类Long
长整型占用64位,最大存储是(2^64)-1,存储范围是(-2^63)-(2^63)-1
3.数据类型的转换
1.自动转换
小数据转为大数据是自动转换
自动转换的关系:byte - > short -> char ->int ->long ->float->double
2.强制转换
他是自动转换的逆序操作,大数据转为小数据位强制转换,在转换的过程中可能出现京都丢失的情况和数据溢出
4.拆箱与装箱
自动装箱就是将基本数据类型转为对应的包装类
Integer a=10
自动拆箱就是将包装类转为对应的基本数据类型
int b = a
注意:自动拆箱的过程中,当包装类进行范围比较时数值在-128-127可以使用==来比较,超过这个范围就要使用equals来比较
5.缓存池
jdk8的默认缓存池是-128~127
Integer.valueof和 new Integer()的区别是在前者如果数据在缓冲池内直接获取缓存池中的值,new Integer()是每次创建一个新的对象
integer.valueOf源码如下:
2.String类型
1.概念
java.lang.String类用于描述字符串
String类型被声明为 final,因此它不可被继承
内部使用 char 数组存储数据,char数组被定为final类型,从jdk1.9开始该类的底层不使用char[]来存储数据,而是改成 byte[]加上编码标记,从而节约了一 些空间
2.不可变的优点
1.线程安全:因为不可变的对象不能被改变所以是应用多线程之间的自由共享
2.网络安全性:因为String是不可变的,所以可以保证传入的参数不被改变。
3.缓存hashcode:字符串的hashcode在Java中经常使用。例如,在HashMap或HashSet中。不可变保证了hashcode总是相同的,只需要计算一次即可
4.String Pool 的需要:String pool 是存储在Method Area(方法区)的一个独特区域,当创建一个String时,如果pool已经存在,则直接引用pool中的常量
3.StringBuffer和StringBuilder
首先我们知道String是不可变的而且是线程安全的
StringBuffer和StringBuilder都是可变的,区别在于StringBuffer内部依靠synchronized关键字进行同步所以是线程安全的,StringBuilder不是线程安全的
4.String保证相同字符串引用的是同一对象
String.intern()
代码示例
String a = new String("23456");
String b = new String("23456");
System.out.println(a == b); // false
String c = a.intern();
System.out.println(a.intern() == c); // true
3.java中参数传递和隐式转换、强制转换
1.参数传递:
java在参数传递的时候无论是普通数据类型还是String数据类型还是对象传参都是值传递
2.隐式转换 :
从小到大,可以隐式转换,数据类型将自动提升。
byte,short,char -->int -->long -->float -->double
3.强制转换:
从大到小(如果你明确知道数据是可以用该数据类型来表示的,可以用强制转换)否则会报错
例如下面伪代码:第一个报错的原因是 s=s+1;s+1会隐式转换为int类型,当把一个int类型赋值给short类型是,可能会损失
第二个不会报错 因为s+=1,虽然可以看做s=s+1,但是还是有区别的,s+=1中有一个强制转换,即s=(short)(s+1),所以不会报错
//第一个会报错
short s = 1;
s= s +1;
//第二个可以正常编译
short s = 1;
s+=1;
4.继承和实现
1.继承权限
java权限有public,protected,private以及包访问权限四种,在继承时可以继承的权限有public,protected和包内访问三种:public和protected所子类可以继承,包内访问权限只能是同包内继承。
关于访问权限:当子类和父类不再一个包内时,子类同样会继承protected方法,但是当子类不覆盖protected方法时,子类同包的类是访问不了该函数的,但是浮类同包内的类可以访问。如果子类覆盖了该方法,结果相反,子类同包的类可以访问,父类同包内的类不能访问。
其中原因是:不覆盖时调用的是父类的方法,子类包内的类与父类不在一个包内所以不能访问,当覆盖后调用的是子类的方法,子类包内的类可以访问,父类包内的类不同包不能访问。
2.抽象类与接口
1.抽象类
抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,有抽象方法的类一定是抽象类。
抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类
2.接口
接口是抽象类的一个特例,在 Java 8 之前,它可以看成是一个完全抽象的类,不能有任何方法实现。
从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了
接口的字段默认都是 static 和 final 的
接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected
5.Object 方法
1.equals()方法
1.五个原则或者关系
1.自反性。对于任何非null的引用值x,x.equals(x)必须返回true
2.对称性。对于任何非null的引用值x和y,当且仅当y.equals(x)必须返回true时,x.equals(y)必须返回true
3.传递性。对于任何非null的引用值x,y和z,当且仅当x.equals(y)必须返回true时,y.equals(z)必须返回true,那么x.equals(z)必须返回true
4.一致性。对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一直返回true,或者一直返回false
5.对于任何非null得引用值x,x.equals(null)必须返回false
2.equals() 与 ==
1.对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法
2.对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价
3.Object类中定义的equals方法和 == 的作用是相同的:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
2.hashCode()
原则:
1.hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,反正则不一定。
2.我们在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等
3.clone()
1.cloneable接口
要使类具有克隆能力能力时,需要实现Cloneable接口,实现它的目的是作为一个对象的一个mixin(混入)接口,表明这个对象是允许克隆的,如果没有实现cloneable接口调用clone方法会报错CloneNotSupportedException。
2. 浅拷贝
拷贝对象和原始对象的引用类型引用同一个对象
实现代码:
public class Demo1 implements Cloneable {
private String name;
private Demo2 demo2
public Demo1(String name,Demo2 demo2) {
this.name = name;
this.demo2=demo2
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Demo2{
}
测试类:
public class CloneTest{
public static void main(String[] args) throws CloneNotSupportedException{
Demo1 d = new Demo1();
d.demo2 = new Demo2();
Demo1 dClone = (Demo1)d.clone();
//输出结果为false
System.out.println(d==dClone);
//输出结果为true
System.out.println(d.demo2==dClone.demo2);
}
}
3.深拷贝
拷贝对象和原始对象的引用类型引用不同对象
实现代码:下面代码输出的测试结果两个都为false
public class Demo1。implements Cloneable {
private String name;
private Demo2 demo2
public Demo1(String name,Demo2 demo2) {
this.name = name;
this.demo2=demo2
}
@Override
public Object clone() throws CloneNotSupportedException {
Demo clone = (Demo1)super.clone();
clone.demo2 = (Demo2)Demo2.clone();
return clone
}
}
public class Demo2 implements Cloneable{
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
6.static相关
1.静态变量
静态变量被所有实例对象共享,在内存中只有一个副本,当且仅当类初次加载时被初始化,可以直接通过类名来访问它。
实例变量: 实例变量是实例对象所拥有的,每创建一个实例就会产生一个实例变量,它与该实例同生共死,存在多个副本,每个实例对象的副本互不影响。
2. 静态方法
1.所谓静态方法,就是方法前面加上static关键字。
2.静态方法在类加载的时候就存在了,它不依赖于任何实例。
3.静态方法必须有实现不能被abstract修饰
4.静态方法内不能有this和super关键字,并且静态方法只能访问所属类的静态字段和静态方法
3.静态语句块 /代码块
静态语句块在类初始化时运行一次,可以有多个,位置可以随便放,它不在任何的方法体内
如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们
4.静态内部类
1.非静态内部类依赖于外部类的实例,而静态内部类不需要。
2.这个静态内部类 的Class实例对象 , 跟 外部类 的 Class实例对象 没有任何关系 , 仅仅 静态内部类 是外部类的一个静态成员而已
3.静态内部类不能直接访问外部类的非静态的变量和方法,必须先创建一个外部类的实例对象,然后才可以访问外部类的实例数据域
public class OuterClass {
class InnerClass {
}
static class StaticInnerClass {
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
//静态内部类
StaticInnerClass staticInnerClass = new StaticInnerClass();
}
}
静态内部类调用外部静态方法和非静态方法
public class OutClass {
private static String a="我是静态变量";
private String b="我是非静态变量";
public void noStatic(){
System.out.println("我是外部非静态方法");
}
public static void hello(){
System.out.println("我是外部静态方法");
}
static class InnerClass{
public void aa(){
hello();//允调用外部静态方法
//noStatic();idea提示报错
System.out.println(a);//允许调用静态变量
//System.out.println(b);报错
OutClass outClass = new OutClass();
outClass.noStatic();//允许调用外部非静态方法
}
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
innerClass.aa();
}
}
输出结果如下:
5.初始化顺序 执行顺序
1.没有子类继承父类的情况
执行顺序:
1. 静态成员变量2. 静态代码块3. 普通成员变量4. 普通代码块5. 构造函数
总结:
1. 静态->普通
2. 变量->代码块->构造函数
3. 构造函数是最后执行的
/**
* @author:
* @date: 2022/4/12 上午
* @description:
*/
public class Parent {
private static String name = "我是静态变量";
private int age = 50;
{
System.out.println("Parent age: "+age);
System.out.println("Parent 我是普通代码块");
}
static{
System.out.println("Parent static name: "+name);
System.out.println("Parent 我是静态代码块");
}
public Parent(){
System.out.println("Parent 我是构造函数");
}
public static void main(String[] args) {
Parent parent = new Parent();
}
}
执行结果如下:
2.子类继承父类的情况
执行顺序:
1. 父类的静态成员变量
2. 父类的静态代码块
3. 子类的静态成员变量
4. 子类的静态代码块
5. 父类的成员变量
6. 父类的代码块
7. 父类的构造函数
8. 子类的成员变量
9. 子类的代码块
10. 子类的构造函数
总结:
1. 先父类再子类
2. 如果子类有静态成员变量和静态代码块,则执行完父类的静态成员变量和静态代码块后,接着执行子类的静态变量和静态代码块,
否则直接按照父类的变量->代码块->构造函数,再执行子类的变量->代码块->构造函数
3. 需要注意的是子类的静态变量和静态代码块是优先于父类的普通成员变量和代码块以及构造函数的。
4. 这也说明了先静态->再普通
/**
* @author:
* @date: 2022/4/12 上午
* @description:
*/
public class Child extends Parent{
private static String name = "我是子类静态变量";
private int age = 26;
{
System.out.println("Child 普通: "+age);
System.out.println("Child 我是普通代码块");
}
static{
System.out.println("Child 静态: "+name);
System.out.println("Child 我是静态代码块");
}
public Child(){
System.out.println("Child 我是构造函数");
}
public static void main(String[] args) {
Child child = new Child();
}
}
输出结果如下: