需要注意的是,在Java中,&&和||具有短路特性。当&&的左操作数为false时,不会计算右操作数;同样地,当||的左操作数为true时,不会计算右操作数。这可以提高程序的效率。
数值型(1)整数类型 :byte、 short 、 int 、lone
(2)浮点类型 :float、double
字符型 :char
布尔型 :boolean
为什么需要包装类:
break语句是用来跳出当前循环的,它会立即终止当前循环,并将控制权返回到循环体之前的位置。通常情况下,我们使用break语句来结束整个循环或者在满足某个条件时退出整个循环。break语句只能跳出一层循环,如果想要跳出多层循环,需要逐层使用break语句。
continue语句是用来跳过本次循环中剩余的部分,直接进入下一次循环。当执行到continue语句时,程序会忽略当前循环中continue后的语句,直接开始下一次循环。continue语句通常用于跳过某些不符合条件的元素,从而快速地遍历整个数组或者链表等数据结构。
return语句是用来结束当前方法并返回到调用该方法的地方。当执行到return语句时,函数会立即停止执行,并将控制权返回给调用该方法的代码段。如果没有指定要返回什么值,那么默认返回null。需要注意的是,return语句只能在方法内部使用,不能在普通代码块中使用。
总结一下:break语句用于跳出整个循环,continue语句用于跳过当前循环中的剩余部分并进入下一次循环,return语句用于结束当前方法并返回到调用该方法的地方。这三个语句虽然都可以用来控制循环的流程,但是它们的作用和使用场景都是不同的。
Java 可以自动对基本数据类型和它们的包装类进行装箱和拆箱。
用一个比喻:面向过程是编年体;面向对象是纪传体。
先后顺序:静态成员变量、成员变量、构造方法。
详细的先后顺序:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数
继承:解决代码冗余的问题,是实现代码重用的重要手段之一
封装:就是将类的状态信息(成员变量)、方法等隐藏在类的内部,不允许外部程序直接访问,而是通过该类的提供的方法来实现对隐藏信息的操作和访问
多态:允许不同类的对象对同一消息作出响应。不同对象调用相同方法即使参数也相同,最终表现行为是不一样的。
优点:
1.良好的封装能够减少耦合,符合程序设计追求'高内聚,低耦合'。
2.类内部的结构可以自由修改。
3.可以对成员变量进行更精确的控制。
4.隐藏信息实现细节。
优点:
1.提高类代码的复用性
2.提高了代码的维护性
优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
重写:
重载:
主要作用2个
具体而言 static 又可分为 4 种使用方式:
抽象类:体现的是 is-a 的关系,如对于 man is a person,就可以将 person 定义为抽象类。
接口:体现的是 can 的关系。是作为模板实现的。如设置接口 fly,plane 类和 bird 类均可实现该1.多继承:⼦类只能继承⼀个直接抽象类;⼦类可以实现多个接⼝
2.实现:⼦类使⽤extends继承抽象类;实现类通过implements实现接⼝
3.成员:抽象类中可以有实例成员、静态成员、抽象⽅法,抽象类中的成员⽅法不能⽤default关键字修饰;接 ⼝中只有常量、抽象⽅法,JDK8之后新增static和default⽅法,9之后新增private⽅法 4.成员变量修饰符: 抽象类可以定义变量,也可定义常量;接⼝中只能定义常量(public static final修饰的常 量)
5.⼦类实现:⼦类在实现抽象⽅法时不允许缩⼩访问权限;实现类在实现抽象⽅法时必须指定public权限
6.构造函数:抽象类可以有构造函数;接⼝中不能定义构造函数
7.最⾼层:类的最⾼层是Object;接⼝没有最⾼层
8.相同点:两者都不能实例化;都是引⽤类型;都可以包含抽象⽅法
总结⼀下 jdk7~jdk9 Java 中接⼝的变化:
在 jdk 7 或更早版本中,接⼝⾥⾯只能有常量变量和抽象⽅法。这些接⼝⽅法必须由选择实现接⼝的类实现。
jdk 8 的时候接⼝可以有默认⽅法和静态⽅法功能。
jdk 9 在接⼝中引⼊了私有⽅法和私有静态⽅法。
(1)引用当前对象的成员变量
(2)调用当前对象的成员方法
(3)调用当前对象的构造方法
静态变量和实例变量的区别?
静态变量: 是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个副本。
实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。
静态⽅法和实例⽅法有何不同?
类似地。
静态方法:static 修饰的方法,也被称为类方法。在外部调⽤静态⽅法时,可以使⽤"类名.⽅法名"的⽅式,也可以使⽤"对象名.⽅法名"的⽅式。静态方法里不能访问类的非静态成员变量和方法。
实例⽅法:依存于类的实例,需要使用"对象名.⽅法名"的⽅式调用;可以访问类的所有成员变量和方法。
final 表示不可变的意思,可用于修饰类、属性和方法:
被 final 修饰的类不可以被继承
被 final 修饰的方法不可以被重写
被 final 修饰的变量不可变,被 final 修饰的变量必须被显式第指定初始值,还得注意的是,这里的不可变指的是变量的引用不可变,不是引用指向的内容的不可变。
== : 它的作⽤是判断两个对象的地址是不是相等。即,判断两个对象是不是同⼀个对象(基本数据类型 == 比较的是值,引⽤数据类型 == 比较的是内存地址)。
equals() : 它的作⽤也是判断两个对象是否相等。但是这个“相等”一般也分两种情况:
默认情况:类没有覆盖 equals() ⽅法。则通过 equals() 比较该类的两个对象时,等价于通过“ == ”比较这两个对象,还是相当于比较内存地址。
自定义情况:类覆盖了 equals() ⽅法。我们平时覆盖的 equals()方法一般是比较两个对象的内容是否相同,自定义了一个相等的标准,也就是两个对象的值是否相等。
因此深拷贝是安全的,浅拷贝的话如果有引用类型,那么拷贝后对象,引用类型变量修改,会影响原对象。
浅拷贝如何实现呢?
Object 类提供的 clone()方法可以非常简单地实现对象的浅拷贝。
深拷贝如何实现呢?
这个也是面试常问——“你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode ⽅法?”
什么是 HashCode?
hashCode() 的作⽤是获取哈希码,也称为散列码;它实际上是返回⼀个 int 整数,定义在 Object 类中, 是一个本地⽅法,这个⽅法通常⽤来将对象的内存地址转换为整数之后返回。
public native int hashCode();
哈希码主要在哈希表这类集合映射的时候用到,哈希表存储的是键值对(key-value),它的特点是:能根据“键”快速的映射到对应的“值”。这其中就利⽤到了哈希码!
为什么要有 hashCode?
上面已经讲了,主要是在哈希表这种结构中用的到。
例如 HashMap 怎么把 key 映射到对应的 value 上呢?用的就是哈希取余法,也就是拿哈希码和存储元素的数组的长度取余,获取 key 对应的 value 所在的下标位置。
为什么重写 equals 时必须重写 hashCode ⽅法?
如果两个对象相等,则 hashcode ⼀定也是相同的。两个对象相等,对两个对象分别调⽤ equals ⽅法都返回 true。反之,两个对象有相同的 hashcode 值,它们也不⼀定是相等的 。因此,equals ⽅法被覆盖过,则 hashCode ⽅法也必须被覆盖。
hashCode() 的默认⾏为是对堆上的对象产⽣独特值。如果没有重写 hashCode() ,则该 class 的两个对象⽆论如何都不会相等(即使这两个对象指向相同的数据)
为什么两个对象有相同的 hashcode 值,它们也不⼀定是相等的?
因为可能会碰撞, hashCode() 所使⽤的散列算法也许刚好会让多个对象传回相同的散列值。越糟糕的散列算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode )。
Java 中有以下四种创建对象的方式:
前两者都需要显式地调用构造方法。对于 clone 机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在 Java 中序列化可以通过实现 Externalizable 或者 Serializable 来实现。
final 用于修饰变量、方法和类:final 修饰的类不可被继承;修饰的方法不可被重写;修饰的变量不可变。
finally 作为异常处理的一部分,它只能在 try/catch
语句中,并且附带一个语句块表示这段语句最终一定被执行(无论是否抛出异常),经常被用在需要释放资源的情况下,System.exit (0)
可以阻断 finally 执行。
finalize 是在 java.lang.Object
里定义的方法,也就是说每一个对象都有这么个方法,这个方法在 gc
启动,该对象被回收的时候被调用。
一个对象的 finalize 方法只会被调用一次,finalize 被调用不一定会立即回收该对象,所以有可能调用 finalize 后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会再次调用 finalize 了,进而产生问题,因此不推荐使用 finalize 方法
两个语句都会去字符串常量池中检查是否已经存在 “abc”,如果有则直接使用,如果没有则会在常量池中创建 “abc” 对象。
但是不同的是,String str1 = new String("abc") 还会通过 new String() 在堆里创建一个 "abc" 字符串对象实例。所以后者可以理解为被前者包含。
String s = new String("abc")创建了几个对象?
很明显,一个或两个。如果字符串常量池已经有“abc”,则是一个;否则,两个。
当字符创常量池没有 “abc”,此时会创建如下两个对象:
Object 类是一个特殊的类,是所有类的父类,也就是说所有类都可以调用它的方法。它主要提供了以下 11 个方法,大概可以分为六类:
对象比较:
对象拷贝:
对象转字符串:
多线程调度:
反射:
垃圾回收:
答案是 a 和 b 相等,c 和 d 不相等。
Integer a= 127 这种赋值,是用到了 Integer 自动装箱的机制。自动装箱的时候会去缓存池里取 Integer 对象,没有取到才会创建新的对象。
如果整型字面量的值在-128 到 127 之间,那么自动装箱时不会 new 新的 Integer 对象,而是直接引用缓存池中的 Integer 对象,超过范围 a1==b1 的结果是 false
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer b1 = new Integer(127);
System.out.println(a == b); //true
System.out.println(b==b1); //false
Integer c = 128;
Integer d = 128;
System.out.println(c == d); //false
}
String 是 Java 基本数据类型吗?
不是。Java 中的基本数据类型只有 8 个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type)。
String 是一个比较特殊的引用数据类型。
String 类可以继承吗?
不行。String 类使用 final 修饰,是所谓的不可变类,无法被继承
JDK 源码里已经对这个方法进行了说明:
*
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*
意思也很好懂:
接口默认方法:Java 8 允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字修饰即可
Lambda 表达式和函数式接口:Lambda 表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中),使用 Lambda 表达式使代码更加简洁,但是也不要滥用,否则会有可读性等问题,《Effective Java》作者 Josh Bloch 建议使用 Lambda 表达式最好不要超过 3 行。
Stream API:用函数式编程方式在集合类上进行复杂操作的工具,配合 Lambda 表达式可以方便的对集合进行处理。
Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。
简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
日期时间 API:Java 8 引入了新的日期时间 API 改进了日期时间的管理。
Optional 类:用来解决空指针异常的问题。很久以前 Google Guava 项目引入了 Optional 作为解决空指针异常的一种方式,不赞成代码被 null 检查的代码污染,期望程序员写整洁的代码。受 Google Guava 的鼓励,Optional 现在是 Java 8 库的一部分。
Lambda 表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。
比如我们以前使用 Runnable 创建并运行线程:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running before Java8!");
}
}).start();
这是通过内部类的方式来重写 run 方法,使用 Lambda 表达式,还可以更加简洁
new Thread( () -> System.out.println("Thread is running since Java8!") ).start();
当然不是每个接口都可以缩写成 Lambda 表达式。只有那些函数式接口(Functional Interface)才能缩写成 Lambda 表示式。
所谓函数式接口(Functional Interface)就是只包含一个抽象方法的声明。针对该接口类型的所有 Lambda 表达式都会与这个抽象方法匹配。
Java8 有哪些内置函数式接口?
JDK 1.8 API 包含了很多内置的函数式接口。其中就包括我们在老版本中经常见到的 Comparator 和 Runnable,Java 8 为他们都添加了 @FunctionalInterface 注解,以用来支持 Lambda 表达式。
除了这两个之外,还有 Callable、Predicate、Function、Supplier、Consumer 等等
Throwable
是 Java 语言中所有错误或异常的基类。 Throwable 又分为Error
和Exception
,其中 Error 是系统内部错误,比如虚拟机异常,是程序无法处理的。Exception
是程序问题导致的异常,又分为两种: