java基础
八大基本数据类型
- 整数的默认类型为int,浮点数的默认类型为double;
- 八种基本数据类型的包装类:除了char的是Character、int类型的是Integer,其他都是首字母大写
- 关于值的范围问题,需要注意char类型是无符号的,不能为负,所以是0开始的;
直接量
- 整数型的直接量默认为int类型
- 浮点型的直接量默认为double类型
类型转换
自动转换:低类型的向高类型的转换
(顺着箭头的方向是可以自动转换的)
强制转换:高类型的向底类型转换,但可能会数据溢出或者精度丢失
基本数据类型对应包装类及使用
java是一门面向对象的语言,但是8中基本数据类型不具备面向对象的特征,所以实际使用中很不便所以为java八种基本数据类型提供了对应的包装类。
- 对应包装类比较特殊的就是int对应的Integer和char对应的Character;
- 对应包装类的直接父类:前6个由于是数,直接父类为Number,而后两个的直接父类就是Object类;
静态方法 valueOf()
- 参数为基本数据类型,返回包装类对象;
- 参数为String字符串(Character类没有以String为 参数的该方法),返回包装类对象;
@Test
public void a() {
/*1.参数为基本数据类型
* 作用:将基本数据类型转换为对应包装类 * */
Integer i=Integer.valueOf(10);
System.out.println(i);//输出10
/*2.参数为String字符串时,
* 作用:返回指定字符串值的包装类对象
* */
Integer a=Integer.valueOf("100");
System.out.println(a);//输出100
Integer b=Integer.valueOf("100a")为
System.out.println(b);//运行错误,字符串的值不少一个int类型的
}
静态方法parseXXX(String str)
- Character类没有该方法;
- 作用:将字符串装换为对应的基本数据类型(注意此处和上面的valueOf方法返回值的不同);
@Test
public void b() {
/*作用:将给定字符串装换为对应的基本数据类型
* 前提是该字符串必须正确描述该基本数据类型表示的值*/
int a=Integer.parseInt("100");
System.out.println(a);//输出100
int b=Integer.parseInt("100a");
System.out.println(b);//运行错误,字符串的值不为int类型
}
非静态方法XXXValue()
- 因为是非静态方法,所以不能像上面两个方法用类名调用了;
- 数字类的包装类(八种包装类中父类是Number的的六个类)才有该方法;
- 作用:将当前包装类对象转换为对应的基本数据类型;
@Test
public void c() {
/*作用:将包装类对象转换为对应的基本数据类型*/
Integer a=Integer.valueOf(100);//将基本数据类型转换为包装类对象
int b=a.intValue();//将包装类对象转换为对应的基本数据类型
System.out.println(b);//输出100
Double c=Double.valueOf(2.33);
double d=c.doubleValue();
System.out.println(d);
}
自动拆箱与装箱
- 自动拆箱 包装类——>基本数据类型 (原理是调用了xxxValue方法)
- 自动装箱 基本数据类型——>包装类 (原理是调用了valueOf方法)
@Test
public void d() {
/*自动装箱:valueOf*/
Integer i=123;//原理是 Integer i=Integer.valueOf(123);
/*自动拆箱*/
int i1=i+1;//原理是 int i1=i.intValue()+1;
Integer a=123;
Integer b=123;
Integer c=a+b;
/*原理为Integer c=Integer.valueOf(a.intValue()+b.intValue());*/
}
面试题
类型转换
public static void main(String[] args) {
int a=10;
double b=3.4;
System.out.println(a>b?a:b);
System.out.println(a);
}
/*输出:10.0 10
解析:这里是一个很容易让人不注意的类型转化,这里a与b参与了运算,
所以类型向类型大的方向转化,10就变成了10.0,但是a本身是没有变化的*/
+=的情况
public static void main(String[] args) {
short a=1; //第一行
a=a+1; //第二行
a+=1; //第三行
}
/*第几行的代码会出错?
答案:第二行会出错,由于a+1变为了int类型,而int类型不能直接赋值给short类型
但是+=这种情况是特殊的,所以不会出错;
*/
自动装箱
- 包装类和基本数据类型比较时,只要值相等就相等
public static void main(String[] args) {
Integer a1=127;
Integer a2=127;
int a3=127;
Integer b1=128;
Integer b2=128;
int b3=128;
System.out.println(a1==a2);
System.out.println(a1==a3);
System.out.println(b1==b2);
System.out.println(b1==b3);
}
/*输出:true true false true
解析:自动装箱时采用valueOf方法,由于127在静态数组的范围内,所以不是new的,
而128的两个引用是指向new出现对象的,所以第一个是true,第三个是false。
而包装类和基本数据类型比较时,只要数值是相等的,就相等
*/
char类型存储汉字
char类型能不能存储一个汉字?为什么?
解析:能,char类型采用的是Unicode编码,Unicode编码包含汉字,所以char类型自然是可以存储一个汉字的
浮点数精度问题
public static void main(String[] args) {
System.out.println(0.1*3==0.3);
System.out.println(0.1*4);
}
/*输出:false 0.4
解析:有些浮点数不能准确的表示出来,与整数相乘之后出精度丢失,常见为小数位含3的
*/
String 是最基本的数据类型吗
不是,String是引用类型,底层维护的是char类型的数组
switch…catch 结构中,switch()括号里都可以传递什么类型的参数
byte short char int String(JDK1.7以后可以) enum
面向对象
面向对象是以对象为核心来思考,解决问题的一种方式,它是 Java 核心的一种思想;
世间的万事万物我们都可以理解成一个对象,所以才有我们万物皆对象的说法。
1. 谈谈面向过程和面向对象
面向过程:就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
举例来说,怎么把大象装进冰箱
第一步:工作人员去打开冰箱门
第二步:把大象塞进冰箱
第三步:工作人员把冰箱门关上
面向对象:就是把现实中的事物都抽象为“对象”。每个对象是唯一的,且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。
举例来说,怎么把大象装进冰箱
冰箱作为一个对象;
大象作为一个对象。
冰箱有这些功能:开门、装物体、关门
面向对象是以对象为核心来思考,解决问题的一种方式,它是 Java 核心的一种思想;
世间的万事万物我们都可以理解成一个对象,所以才有我们万物皆对象的说法。
优点:相比面向过程易维护、易复用、易扩展,但是因为类调用时要实例化,所以开销大性能比面向过程低
2. 面向对象的有哪些特征?以及你对这些特征的理解
面向对象三大特征:封装,继承,多态
封装
- 类:封装的是对象属性和行为
- 方法:封装的是具体的业务逻辑功能
- 访问控制修饰符:封装的是访问权限
为什么要有封装
- 灵活控制属性的读和修改
比如我只能让使用者知道这狗狗的名字,但却不允许修改他。那就把setName()设为private私有,getName()设为public公有。 - 防止使用者错误修改属性
直接调用类的属性,万一改错了怎么办。所以我们可以在公有方法setName()里面添加限制条件,防止使用者错误修改属性。 - 有助于对象封装内部的实现细节
继承
继承是一种利用已有的类快速创建新的类的一种机制
- 作用:代码的复用
- 父类:所有子类共有的行为和属性
子类:子类所特有的属性和行为 - 子类继承父类后,子类具有:父类+子类
- 单一继承,多实现,继承是具有传递性的
重写和重载的区别
重载(overload)
- 发生在 同一个类 中,方法名相同,参数列表不同,方法体不同
- 与返回值类型无关
- 编译期绑定
重写(override)
发生在 父子类 中,方法名称相同,参数列表相同(方法名称+参数列表=方法的签名)方法体不同
重写遵循两同两小一大
1)两同:方法名相同,参数列表相同(方法的签名)
2)两小:(返回值类型、异常、访问权限)
A. 子类方法的返回值小于等于父类方法的返回值
a. void/基本类型返回值必须相同
b. 引用类型的返回值小于等于父类的返回值(父类大,子类小)
B. 子类方法抛出的异常小于或等于超类的方法抛出的异常
3)一大:子类方法的访问权限大于或等于父类方法的访问权限
运行期绑定
注意:构造方法不能重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是可以被再次声明;
多态
多态的表现形式:
行为的多态(重载,重写),
对象的多态(向上造型,强制类型转换(instanceof)/向下转型)
构造器(构造方法)是如何工作的?
- 分配对象空间,并将对象初始化为0或null,Java不允许用户操作一个不定值对象
- 执行属性值的显式初始化(将属性值(成员变量)赋值给对象)
- 执行构造器(执行构造器方法里的代码)
- 将变量赋值到堆中的对象上
this 与 super 的区别
this
- this 指代的是当前对象
- this.属性 调用的是当前对象的属性
- this() 是对本类构造函数的调用
必须放在构造方法的第一行,否则会出现编译错误 - this.方法 调用的是当前对象的方法
super
- super 指代的时当前对象父级(直接)对象
- super.属性 指代的是超类对象的属性
- super() 是对超类构造函数的调用
必须放在构造方法的第一行,否则会出现编译错误 - super.方法 调用的是父类对象的方法
static
static 是静态的意思,常用于修饰变量和方法,当然也可以修饰代码块和内部类。
静态的特点:
- 随着类的加载而加载
- 优先于对象存在
- 被所有对象所共享
- 可以被类点名直接调用
注意事项:
- 为什么静态方法只能访问静态成员?
因为静态的内容是随着类的加载而加载,他是先进入内存中的 - 静态方法中不能使用 this,super 关键字(静态方法中没有隐式的 this)
静态代码块,构造代码块,构造方法,局部代码块的执行顺序
静态代码块>构造代码块>构造方法>局部代码块
什么是值传递,什么是引用传递
值传递(pass by value)是指在调用函数时将实际参数的值复制一份传递到函数中,这样在函数中如果对参数的副本进行修改,将不会影响到原来的实际参数值。
引用传递(pass by reference)是指在调用函数时将实际参数的引用地址(引用的对象在堆中的内存地址)直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。
错误理解二:Java是引用传递。
错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。
java中无论是基本类型还是引用类型,都是值传递:
- 对于基本类型而言,传递的是具体的值的副本
- 对于引用类型而言,传递的是具体的地址值的副本
深拷贝和浅拷贝
浅拷贝:浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存
深拷贝:深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。
成员变量与局部变量的区别
变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域。
成员变量:类里方法外的变量,我们称为成员变量
局部变量:方法中的变量,我们称为局部变量
区别:
- 作用域:
成员变量:针对整个类有效
局部变量:只在某个范围内有效(一般指的时方法内或者代码块内) - 存储位置:
成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。 - 生命周期
成员变量:随着对象的创建而存在,随着对象的消失而消失。
局部变量:当方法调用完,或者语句结束后,就自动释放。 - 初始值
成员变量:有默认初始值。
局部变量:没有默认初始值,使用前必须赋值。 - 使用原则
在使用变量时需要遵循的原则为:就近原则。即:首先在局部范围找,有就使用;接着在成员位置找。
静态变量与实例变量的区别
- 调用方式:
静态变量也称为类变量,可以直接通过类名点调用,也可以通过对象名点(引用)调用[但是不建议]。这个变量是属于类的。
实例变量只能通过对象名(引用)点调用,这个变量属于对象的。 - 储存方式:
静态变量在类加载到内存的时候会对其进行分配空间,且只会分配一次内存空间,以后对该静态变量的操作都是在这一块内存上完成的,这块内存空间我们成为方法区/静态区。
成员变量时在每创建一个对象时,都会对该对象的实例变量进行分配空间,这块内存空间我们成为堆内存。 - 生命周期:
静态变量随着类的加载而存在,随着类的消失而消失,声明周期较长
成员变量随着对象的创建而存在,随着对象的消失而消失 - 与对象的相关性:
静态变量是所有对象共享的数据
实例变量是每个对象所特有的数据
JDK8新特性
- 函数式接口
- Stream API
- 接口中的默认方法和静态方法
- 新时间日期API
- Optional
JDK 中常用的包有哪些
- java.lang
这个是系统的基础类,使用时不需要导包 - java.util
这个是系统的工具类,起到了辅助作用 - java.io
这里是所有和输入输出有关的类,包括文件的相关操作 - java.net
这里是与网络有关的类 - java.sql
这个是与数据库操作的类
Lambda 表达式什么时候用
当我们想使得我们的代码更简洁更灵活,我们使用 Lambda 表达式;
只有满足只有一个抽象方法的抽象类或者接口我们才能使用 Lambda 表达式
所有的接口都支持 Lambda 表达式嘛
Lambda表达式只能用于接口中只有一个抽象方法的情境中,一般我们称这种接口为函数式接口
面试题
接口和抽象类的区别
- 相似点:
(1)接口和抽象类都不能被实例化
(2)实现接口或继承抽象类的普通子类都必须实现这些抽象方法 - 不同点:
(1)抽象类可以包含普通方法和代码块,接口里只能包含抽象方法,静态方法和默认方法,
(2)抽象类可以有构造方法,而接口没有
(3)抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值
==和equals的区别
==比较基本类型,比较的是值,==比较引用类型,比较的是内存地址
equlas是Object类的方法,本质上与==一样,但是有些类重写了equals方法,比如String的equals被重写后,比较的是字符值,另外重写了equlas后,也必须重写hashcode()方法
补充:==对于基本类型来说,==⽐较的是值是否相等;对于引⽤类型来说,== ⽐较的是两个引⽤是否指向同⼀个对象地址(两者在内存中存放的地址(堆内 存地址)是否指向同⼀个地⽅)equals对于引⽤类型(包括包装类型)来说,equals 如果没有被重写,对⽐它们的地址是否相等;如果 equals()⽅法被重写(例如 String ),则⽐较的是地址⾥的内容。
hashCode和equals
hashCode()和equals()都是Object类的方法,hashCode()默认是通过地址来计算hash码,但是可能被重写过用内容来计算hash码,equals()默认通过地址判断两个对象是否相等,但是可能被重写用内容来比较两个对象
所以两个对象相等,他们的hashCode和equals一定相等,但是hashCode相等的两个对象未必相等
如果重写equals()必须重写hashCode(),比如在HashMap中,key如果是String类型,String如果只重写了equals()而没有重写hashcode()的话,则两个equals()比较为true的key,因为hashcode不同导致两个key没有出现在一个索引上,就会出现map中存在两个相同的key
补充:1. 如果两个对象相等,则 hashcode ⼀定也是相同的2. 两个对象相等 , 对两个 equals ⽅法返回 true3. 两个对象有相同的 hashcode 值,它们也不⼀定是相等的4. 综上, equals ⽅法被覆盖过,则 hashCode ⽅法也必须被覆盖5. hashCode() 的默认⾏为是对堆上的对象产⽣独特值。如果没有重写 hashCode() ,则该 class 的 两个对象⽆论如何都不会相等(即使这两个对象指向相同的数据)
String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。
StringBuffer可变并且线程安全
StringBuiler可变但线程不安全。
操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer。
Java创建对象得五种方式?
(1)new关键字 (2)Class.newInstance (3)Constructor.newInstance (4)Clone方法 (5)反序列化
什么是反射?
反射是通过获取类的class对象,然后动态的获取到这个类的内部结构,动态的去操作类的属性和方法。
应用场景有:要操作权限不够的类属性和方法时、实现自定义注解时、动态加载第三方jar包时、按需加载类,节省编译和初始化时间;
获取class对象的方法有:class.forName(类路径),类.class(),对象的getClass()
Java 的内存分为两类,一类是 栈内存 ,一类是 堆内存 。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。
堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用 new 创建的对象都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用 final 修饰后,放在堆中,而不是栈中。
instanceof
是 Java 的保留关键字,它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean
的数据类型
instanceof
关键字一般用于强制转换,在强转之前用它来判断是否可以强制转换
简单类对象的实例化(内存模型)
(1.问题:person p = new person()如何实例化的?
过程:
(1.方法区:加载person.class
(2.栈内存:在栈中申请空间,声明变量p (p BE2500)
(3.堆内存:new person在堆内存中开辟空间、分配地址。(假如地址BE2500)
(4.堆内存:并在对象空间中,对对象中的属性进行默认初始化,此时age=0;name=null,sex=0,类成员变量显示初始化,此时age=1,name=“zhangsun”,sex=0(显示初始化:=右边向左边赋值)
(5.栈内存,构造函数的方法进栈,进行初始化
(6.栈内存,初始化完毕后,将堆内存中的地址赋值给引用变量,构造方法出栈;
子类对象的实例化
Student stu = new Student();实现过程:
过程
(1. 方法区:先加载父类person.class,再sstudent.class
(2.栈内存: 在栈中申请空间,声明stu
(3. 堆内存:在堆内存中开辟空间,分配地址。
(4.堆内存:并在对象空间中,对对象的属性(包括父类的属性)进行初始化。
(5.栈内存:子类构造器函数方法进栈
(6.堆内存:显示初始化父类的属性
(7.栈内存:父类构造方法进栈执行完毕出栈
(8.堆内存:显示初始化子类的属性
(9.栈内存:初始化完毕后,将栈内存中的地址赋值给引用变量,子类构造方法出栈