一、面向对象高级基础
1.Java的动态绑定机制
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
System.out.println(a.sum());//40 子类sum()注释后-> 30
System.out.println(a.sum1());//30 子类sum1()注释后-> 20
}
}
class A {//父类
public int i = 10;
//动态绑定机制:
public int sum() {//父类 sum()注释后
return getI() + 10;//调用子类getI()->20 + 10
}
public int sum1() {//父类 sum1()注释后
return i + 10;//属性还是用本类->10 + 10
}
public int getI() {//父类 getI
return i;
}
}
class B extends A {//子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {//子类 getI()
return i;
}
// public int sum1() {
// return i + 10;
// }
}
2.Object类方法详解
2.1==和 equals 的对比 [面试
- ==
public class Equals01 {
public static void main(String[] args) {
//引用类型,比较地址
A1 a = new A1();
A1 b = a;
A1 c = b;
System.out.println(a == c);//true
System.out.println(b == c);//true
B1 bObj = a;
System.out.println(bObj == c);//true
//基本数据类型,判断值是否相等
int num1 = 10;
double num2 = 10.0;
System.out.println(num1 == num2);
}
}
class B1 {}
class A1 extends B1 {}
- equals
Integer integer1 = new Integer(1000);
Integer integer2 = new Integer(1000);
System.out.println(integer1 == integer2);//false
System.out.println(integer1.equals(integer2));//true
String str1 = new String("hspedu");
String str2 = new String("hspedu");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
2.2hashCode方法
2.3toString方法
2.4finaliz方法
3.匿名内部类
3.1本质
3.2基于接口的匿名内部类
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 { //外部类
private int n1 = 10;//属性
public void method() {//方法
//基于接口的匿名内部类
//1.需求:想使用 IA 接口,并创建对象
//2.传统方式,是写一个类,实现该接口,并创建对象
//3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger 的编译类型 IA
//6. tiger 的运行类型就是匿名内部类 Outer04$1
/*
我们看底层 会分配类名:Outer04$1
class Outer04$1 implements IA {
@Overrid
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//7. jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1 实例,并且把地址返回给 tiger
//8. 匿名内部类使用一次,就不能再使用
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger 的运行类型=" + tiger.getClass());
tiger.cry();
}
}
interface IA {//接口
public void cry();
}
//class Tiger implements IA {
//
// @Override
// public void cry() {
// System.out.println("老虎叫唤...");
// }
//}
3.3基于类的匿名内部类
package org.example;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 { //外部类
private int n1 = 10;//属性
public void method() {//方法
//演示基于类的匿名内部类
//分析
//1. father 编译类型 Father
//2. father 运行类型 Outer04$2
//3. 底层会创建匿名内部类
/*
class Outer04$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了 test 方法");
}
}
*/
//4. 同时也直接返回了 匿名内部类 Outer04$2 的对象
//5. 注意("jack") 参数列表会传递给 构造器
Father father = new Father("jack"){
@Override
public void test() {
System.out.println("匿名内部类重写了 test 方法");
}
};
System.out.println("father 对象的运行类型=" + father.getClass());//Outer04$2
father.test();
}
}
class Father {//类
public Father(String name) {//构造器
System.out.println("接收到 name=" + name);
}
public void test() {//方法
}
}
3.4细节
4.代码块
4.1基本介绍
4.2注意事项
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//1. 创建对象实例时(new)
// AA aa = new AA();
//2. 创建子类对象实例,父类也会被加载, 而且 父类先被加载,子类后被加载
AA aa2 = new AA();
//3. 使用类的静态成员时(静态属性,静态方法)
System.out.println(Cat.n1);
//4. static 代码块,是在类加载时,执行的,而且只会执行一次.
// DD dd = new DD();
// DD dd1 = new DD();
//5. 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
System.out.println("----------");
//6. 如果只是使用类的静态成员时,普通代码块并不会执行
System.out.println(DD.n1);//8888, 静态代码块一定会执行
}
}
class AA extends BB{
static { //父类先被加载,子类后被加载
System.out.println("AA类的静态代码快执行了..");
}
}
class BB {
static { //父类先被加载,子类后被加载
System.out.println("BB类的静态代码快执行了..");
}
}
class Cat {
static int n1 = 11;
static {
System.out.println("Cat类的静态代码快执行了..");
}
}
class DD {
static int n1 = 888; //只是使用类的静态成员时,普通代码块并不会执行,静态代码块一定会执行
//static 代码块,是在类加载时,执行的,而且只会执行一次.
static {
System.out.println("DD类的静态代码快执行了..");
}
//普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。
{
System.out.println("DD类的普通代码快执行了..");
}
}
4.3调用顺序 [重点
public class CodeBlockDetail02 {
public static void main(String[] args) {
//1. 静态代码块和静态属性初始化调用的优先级一样,按他们定义的顺序调用
//2. 普通代码块和普通属性初始化调用的优先级一样,按他们定义的顺序调用
//3. 构造器
A a = new A(); // (1) A 静态代码块 01 (2) getN1 被调用....(3)A 普通代码块 01 (4)getN2 被调用...(5)A() 构造器被调用
//构造器 父类代码块、构造器->子类代码块、构造器
//BB2 bb = new BB2();
}
}
class A {
//无参构造器
public A() {
System.out.println("A() 构造器被调用");
}
{ //普通代码块
System.out.println("A 普通代码块 01");
}
private int n2 = getN2();//普通属性的初始化
static { //静态代码块
System.out.println("A 静态代码块 01");
}
private static int n1 = getN1();//静态属性的初始化
public static int getN1(){
System.out.println("getN1被调用");
return 100;
}
public int getN2() { //普通方法/非静态方法
System.out.println("getN2 被调用...");
return 200;
}
}
class BB2 extends A {
BB2 (){
System.out.println("Bb() 构造器被调用");
}
{
System.out.println("Bb 普通代码块 01");
}
}
4.4继承调用顺序
5.单例设计模式
5.1饿汉式--类加载时,对象实例就创建了
public class SingleTon01 {
public static void main(String[] args) {
/**
* 如何保障 只创建一个 GirlFriend对象
* 单例模式-饿汉式:类加载时,对象就创建了
* 1.构造器私有化
* 2.在类的内部直接创建对象(static)
* 3.向外暴露一个获取对象的方法 getInstance (static)
*/
GirlFriend gf1 = GirlFriend.getInstance();
GirlFriend gf2 = GirlFriend.getInstance();
System.out.println(gf1);
System.out.println(gf2);
System.out.println(gf1 == gf2); //T
}
}
class GirlFriend {
private String name;
private GirlFriend(String name){
this.name = name;
}
//饿汉式--类加载时,就创建了
private static GirlFriend gf = new GirlFriend("韩孝周");
public static GirlFriend getInstance(){
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
5.2懒汉式--使用时,才创建对象实例
public class SingleTon02 {
public static void main(String[] args) {
/**
* 单例模式-懒汉式
*只有在调用方法时,才会实例化对象,后面再次调用时,会返回上次创建的Cat对象
*/
System.out.println(Cat.n1); //只有调用方法时,对象才会实例化
// Cat c1 = Cat.getInstance();
// Cat c2 = Cat.getInstance();
// System.out.println(c1);
// System.out.println(c2);
// System.out.println(c1== c2); // T
}
}
class Cat {
private String name;
public static int n1 = 9;
private Cat(String name) {
System.out.println("构造器被调用了。。。");
this.name = name;
}
private static Cat cat; //默认是null
//懒汉式--只有在调用方法时,才会实例化对象
public static Cat getInstance(){
if (cat == null){
cat = new Cat("mimi");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
5.3饿汉式 VS 懒汉式
6.final关键字
6.1基本介绍
6.2注意事项
7.抽象类、接口
7.1抽象类
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
一个类中存在抽象方法,则该类必须声明为abstract类,即
抽象类不一定有抽象方法,但是抽象方法必须是抽象类
5)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
6)抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。
原因:Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。
7.2抽象模板设计模式
7.3接口
7.4接口的多态特性
7.5接口 VS 继承类
小结:
- 当子类继承了父类,就自动的拥有父类的功能
- 如果子类需要扩展功能,可以通过实现接口的方式扩展。
- 可以理解实现接口是对java单继承机制的一种补充.
二、常用类
1.八大包装类
1.1包装类的分类
1.2.装箱和拆箱
PS:面试题练习
三元运算符是一个整体,输出的结果会转换成精度最高的类型
1.3.包装类的方法
1.4Integer创建机制
==比较
1.new则是创建了新对象,则地址不相同
2.Integer.valueOf(num) 若num在范围-128~127内,则直接返回比较的是值,若在范围外,则new
3. 若Integer和int比较,只要有基本数据类型(这里是int),比较的就是值
public class IntegerExercise01 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //False new 即创建新对象,地址不同
//在范围-128~127直接返回,超范围才会new
Integer m = 1; //底层 Integer.valueOf(1);
Integer n = 1;//底层 Integer.valueOf(1);
System.out.println(m == n); //True 1在-128~127范围内直接返回
/*
public static Integer valueOf(int i) {
//low = -128 high = 127
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)]; // low <= i <= high 直接返回
return new Integer(i); //超范围才new
}
*/
Integer x = 128;//底层 Integer.valueOf(1);
Integer y = 128;//底层 Integer.valueOf(1);
System.out.println(x == y);//False 超范围new
}
}
1.5Integer面试题
2.String类
2.1String的理解
String s = new String();
1. String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】
接口 Comparable [String 对象可以比较大小]
2. String 是 final 类,不能被其他的类继承
/* public final class String
implements java.io.Serializable, Comparable<java.lang.String>, CharSequence {
*/
3. String 有属性 private final char value[]; 用于存放字符串内容
/** The value is used for character storage. */
/*private final char value[];
*/
4. 一定要注意:value 是一个 final 类型, 不可以修改(需要功力):
// 即 value 不能指向新的地址,但是单个字符内容是可以变化
final char[] value = {'a', 'b', 'c'};
char[] v2 = {'t', 'o', 'm'};
//value = v2; 报错
2.2创建 String对象的两种方式及区别
PS:练习题
2.3字符串的特性
PS:练习题
1) 栈: 一般存放基本数据类型(局部变量)
2) 堆: 存放对象(Cat cat , 数组等)
3) 方法区:常量池(常量,比如字符串), 类加载信息
该题:main里的对象是局部变量,存在栈里
str是Test01的成员属性,存在堆里
2.4String类的常用方法
//1.equals 区分大小写,判断内容是否相等
String str1 = "abc";
String str2 = "Abc";
System.out.println(str2.equals(str1));
//2.equalslgnoreCase 忽略大小写的判断内容是否相等
String s1 = "abc";
String s2 = "aBc";
System.out.println(s2.equalsIgnoreCase(s1));
//3.length 获取字符的个数,字符串的长度
System.out.println(s2.length());
//4.indexOf 获取字符在字符串中第1次出现的索引索引从0开始,如果找不到,返回-1
String str3 = "1fdj@ig4nb@bd";
System.out.println(str3.indexOf("@"));
//5.lastIndexOf 获取字符在字符串中最后1次出现的索引,索引从0开始,如找不到,返回-1
System.out.println(str3.lastIndexOf("@"));
//6.substring 截取指定范围的子串 下标从0开始,截取beginIndex及之后的字符串
String s3 = "hello,tom!";
System.out.println(s3.substring(6));
//substring(beginIndex,endIndex) 截取beginIndex到endIndex-1的字符串
System.out.println(s3.substring(6,9)); //tom
//7.trim 去前后空格
String s4 = " hello ";
System.out.println(s4.trim());
//8.charAt:获取某索引处的字符,注意不能使用Str[index]这种方式.
String s5 = "hello";
System.out.println(s5.charAt(1));
System.out.println("============");
//9.toUpperCase、toLowerCase 转大写和转小写
String s6 = "Hello";
System.out.println(s6.toUpperCase());
System.out.println(s6.toLowerCase());
//10.concat 拼接字符串
String str6 = "hello";
System.out.println(str6.concat(" world").concat("!"));
//11.replace替换字符串中的字符
String str7 = "hello world";
System.out.println(str7.replace("world", "世界!"));
//12.split分割字符串,对于某些分割字符,我们需要转义比如| \\等
String poem ="锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
String[] split = poem.split(",");//以“,"分割字符串为字符串数组
for (int i = 0; i < split.length; i++) {
System.out.println(split[i]);
}
//注意!:特殊字符需要使用 转义符\ 分割
poem = "E:\\a\\b\\c.txt";
split = poem.split("\\\\");//以“\\”分割字符串为字符串数组
//13.compareTo 比较两个字符串的大小,前者大返回正数,后者大返回负数,相等返回0
String s7 = "jack";
String s8 = "jcck";
System.out.println(s7.compareTo(s8));//-2 'c'-'a'= 2
//14.toCharArray 转换成字符数组
String s9 = "hello";
char[] chars = s9.toCharArray();
for (int i = 0; i < chars.length; i++) {
System.out.println(chars[i]);
}
System.out.println("========");
//15.format 格式字符串,%s字符串 %c字符 %d整型 %.2f浮点型
// 案例,将一个人的信息格式化输出.
String name = "john";
int age = 10;
double score = 56.857;
char gender = '男';
//将所有的信息都拼接在一个字符串.
String info = "我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我! ";
System.out.println(info);
//使用String.format格式化字符串
String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, name, age, score, gender);
System.out.println("info2=" + info2);
3.StringBuffer类
3.1StringBuffer结构
- 1. StringBuffer 的直接父类 是 AbstractStringBuilder
- 2. StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化
- 3. 在父类中 AbstractStringBuilder 有属性 char[] value,不是 final,该 value 数组存放 字符串内容,引出存放在堆中的
- 4. StringBuffer 是一个 final 类,不能被继承
- 5. 因为 StringBuffer 字符内容是存在 char[] value, 所有在变化(增加/删除) ,不用每次都更换地址(即不是每次创建新对象), 所以效率高于 String
3.2String与StringBuffer之间的转换
3.3StringBuffer常用方法
StringBuffer s = new StringBuffer("hello");
//1.增--append
s.append(",world").append("!").append(true).append(10.3);//hello,world!true10.3
System.out.println(s);
//2.删--delete [5,11)
s.delete(5,11);//删除下标5到下标10的字符串
System.out.println(s);
//3.改--replace [6,10) 替换下标6到下标9的字符串
s.replace(6,10,"java");//hello!java10.3
System.out.println(s);
//4.indexOf--查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("java");
System.out.println(indexOf); //6
//5.插--insert 在索引为6的位置插入字符串"来",原索引为6的字符串"java"后移
s.insert(6, "来");
System.out.println(s);
//6.长度--length()
System.out.println(s.length());
4.StringBuilder类
4.1StringBuilder结构
- 1. StringBuilder 继承 AbstractStringBuilder 类
- 2. 实现了 Serializable ,说明 StringBuilder 对象是可以串行化(对象可以网络传输,可以保存到文件)
- 3. StringBuilder 是 final 类, 不能被继承
- 4. StringBuilder 对象字符序列仍然是存放在其父类 AbstractStringBuilder 的 char[] value; // 因此,字符序列是堆中
- 5. StringBuilder 的方法,没有做互斥的处理,即没有 synchronized 关键字,因此在单线程的情况下使用
4.2String、StringBuffer 和 StringBuilder 的比较
StringVsStringBufferVsStringBuilder.java 效率 : StringBuilder > StringBuffer > String
long startTime = 0L;
long endTime = 0L;
StringBuffer buffer = new StringBuffer("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 80000; i++) {//StringBuffer 拼接 20000 次
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer 的执行时间:" + (endTime - startTime));
StringBuilder builder = new StringBuilder("");
startTime = System.currentTimeMillis();
for (int i = 0; i < 80000; i++) {//StringBuilder 拼接 20000 builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder 的执行时间:" + (endTime - startTime));
String text = "";
startTime = System.currentTimeMillis();
for (int i = 0; i < 80000; i++) {//String 拼接 20000
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String 的执行时间:" + (endTime - startTime));