JDK5的新特性
1 //自动装箱 基本类型--引用类型
2 Integer total = 99;
3
4 //自动拆箱,引用类型--基本类型
5 int totalprim = total;
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0
“+=”是java中的一个运算符,而不是两个,所以在运算时 会进行自动类型转换。所以在编译时没有报错。
a=a+b则必须强转,带来的结果就是精度丢失
在两个变量的数据类型一样时:a+=b 和a=a+b 是没有区别的。
但是当两个变量的数据类型不同时,就需要考虑一下数据类型自动转换的问题了,也就是涉及到精度了。
对于short s1 = 1; s1 = s1 + 1;
由于s1+1运算时会自动提升表达式的类型,所以结果是int型,再赋值给short类型s1时,编译器将报告需要强制转换类型的错误。对于short s1 = 1; s1 += 1;由于 += 是java语言规定的运算符,java编译器会对它进行特殊处理,因此可以正确编译。
2 << 3,
因为将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,而位运算cpu直接支持的,效率最高,所以,2乘以8等於几的最效率的方法是2
<< 3。
1.作用:
让变量的值增加(++)1或者减少(--)1
2.使用格式
(1)写在变量的前面(++a,--a)
(2)写在变量的后面(a++,a--)
3.使用方式
(1)单独使用: 不和其它运算一起,自己独占一行
前++/-- 和 后++/-- 没有任何区别
(2)混合使用: 和其它操作(赋值,打印)一起使用
前++/--: [先++/--,后使用]
后++/--: [先使用,后++/--]
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同(地址值是否相同);
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:
class Cat {
public Cat(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false
输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
原来 equals 本质上就是 ==。
那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:
String s1 = new String(“老王”);
String s2 = new String(“老王”);
System.out.println(s1.equals(s2)); // true
同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
- 总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
不对,两个对象的 hashCode() 相同,equals() 不一定 true。
代码示例:
String str1 = "通话";
String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d", str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));
// 执行的结果:
// str1:1179395 | str2:1179395 false
代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
哈希值和地址值是不一样的,哈希值是通过哈希算法散列得来的,而地址值是通过是和物理层面有关,是系统分配的,是不存在相同的,而哈希值是可以通过强制手段设置为相同的,也就是说哈希值是一种逻辑上的确保唯一性,而地址值就是物理上确保唯一性。
字符串是常量;它们的值在创建之后不能更改。
特点:
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的
String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder
可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而
StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于
StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
不一样,因为内存的分配方式不一样。String str="i"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
两种说法配合着看,其实是一个意思
至少创建一个对象,也可能两个。因为用到new关键字,肯定会在堆中创建一个str2的String对象,它的value是“ABC”。同时如果这个字符串再java String池里不存在,会在java池里创建这个String对象“ABC”。
- 首先在常量池中查找是否存在内容为"abc"字符串对象
- 如果不存在则在常量池中创建"abc",并让str引用该对象
- 如果存在则直接让str引用该对象
壹:方法重载
1.概念:
在同一个类中(目前),如果多个方法的功能相同,只是参数不同,那么可以让多个方法使用相同的名字,这种现象叫做方法重载
2.参数不同有哪些情况
(1)个数不同
(2)类型不同
(3)多个类型,顺序不同
3.方法重载与哪些因素无关
(1)与参数的名称无关
(2)与返回值类型无关
(3)和修饰符无关
贰:方法重写
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效
果,也称为重写或者复写。声明不变,重新实现。
方法重写的注意事项:
1. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
public > protected > 默认(什么都不写) > private
2. 子类方法覆盖父类方法,
返回值类型、函数名和参数列表都要一模一样
(最简单方式的方法重写)。
3. 父类private的方法,子类无法继承,不存在重写
4. 子类方法覆盖父类方法,
返回值类型(子类方法的返回值类型<=父类)
叁:方法重载与方法重写的区别
方法重载(OverLoad):
方法名称相同,参数列表不同
方法重写(Override): 覆盖重写
继承中,子类出现了和父类一模一样的方法
(返回值类型,方法名和参数列表都相同),
方法体{}中的内容不一样
重载:发生在一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同,发生在编译时
重写:发生在父子类中,方法名,参数列表必须相同,返回值必须小于等于父类,抛出异常的范围必须小于等于父类,访问修饰符范围必须大于等于父类,父类为private则不能重写
函数时不能以返回值来区分的,返回值时函数运行之后的一个状态。保持调用这与被调用这之间的通信
1.面向过程:当要实现一个功能时,需要亲力亲为,处理每个细节
2.面向对象:当要实现一个功能时,不关心具体的实现步骤,只关心结果,找一个具有该功能的类,帮我们做事
3.面向对象的思想
(1)面向对象是基于面向过程的编程思想.
(2)面向过程:强调的是每一个功能的步骤
(3)面向对象:强调的是对象,然后由对象去调用功能
1)如果发现,某个数据被该类的所有对象所共同使用,只需要一份,这种数据就需要使用static修饰。
2)被static关键字修饰的数据,和对象无关,依赖于类,内存中只有一份。
3)静态的注意事项
静态的东西,可以使用静态的东西,但是不能使用非静态的东西(静态当中不能使用非静态)
原因:
静态方法由类名调用,目前没有对象,
所以静态方法中不能使用对象中的内容(非静态的)
静态的内容随着类的加载而加载,并进行初始化静态的(先人),非静态的(后人) 静态的内容什么加载到内存中? 1.new对象的时候 2.当使用类中的静态内容的时候 静态方法调用的注意事项: - 静态方法可以直接访问类变量和静态方法 - 静态方法不能直接访问普通成员变量或成员方法 - 非静态方法可以直接访问静态变量或静态方法 - 静态方法中,不能使用this关键字 this代表调用方法的对象 而静态方法由类名调用,没有对象,就没有this的用法
4)静态代码块
格式:
static {
//...
}
特点:
1.优先于构造方法执行,唯一执行一次
2.静态代码块,随着类的加载而加载,而且加载的时候就执行一次
3.静态代码块仍然属于静态的东西,里面不能使用非静态的内容
作用:
1.给静态成员赋值
2.完成项目的初始化动作
不可改变。可以用于修饰类、方法和变量。
类:被修饰的类,不能被继承。
方法:被修饰的方法,不能被重写(可以重载)。
变量:final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
abstract和final不可以同时使用
abstract要求子类必须覆盖重写,而被final修饰的类,其子类不可以覆盖重写。
final修饰变量
1.局部变量
(1)基本类型: 被final修饰的变量中的具体的数据值是不可改变的
只能赋值一次,不能进行第二次赋值
(2)引用类型:被final修饰的应用变量,变量中的地址值不可改变
但是该变量所指向的内存空间中的内容可以改变
2.成员变量(看MyClass类)
(1)定义未赋值:
保证所有构造方法中,必须给final修饰的成员变量赋值
其它的成员方法中,不能修改final修饰的变量的值
(2)定义并赋值
保证所有的构造方法和成员方法中,不能修改final修饰的成员变量的值
向上抽取共性,将共同的内容定义在父类中 共性抽取。
java只支持单继承,不支持多继承,但支持多层继承。
注意:
子类可以使用父类中除了private和构造方法以外的内容
好处:
1.让类与类之间产生了关系
2.提高了代码的复用性
3.是多态的前提
继承中三个重名的成员变量访问特点
1、就近原则
2、this.变量名:本类成员变量
3、super.变量名:父类成员变量
继承中构造方法的访问特点
1.构造方法的名字是与类名一致的。
所以子类是无法继承父类构造方法的
2.子类继承父类,是为了使用父类的内容,
所以子类创建对象调用构造方法时,
必须先调用父类的构造方法,
完成父类成员的初始化动作,
子类才可以使用父类的成员,
super()表示调用父类的空参构造
3.子类的构造方法中如果没有手动给出super调用父类构造,
编译器默认提供一个super()调用父类的空参构造
4.super调用父类构造,只能写在第一句
5.构造方法可以重载,所以:
super(...):调用父类带参数的构造方法
接口是常量值和方法定义的集合。接口是一种特殊的抽象类。
java类是单继承
的。classB Extends classA
java接口可以多继承
。Interface3 Extends Interface0, Interface1, interface……
不允许类多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该继承那一个呢?
但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口。
注意:
1)一个类如果实现了一个接口,则要实现该接口的所有方法。
2)方法的名字、返回类型、参数必须与接口中完全一致。
3)因为接口的方法默认是public类型的,所以在实现的时候一定要用public来修饰(否则默认为protected类型,缩小了方法的使用范围)。
抽象:
不够具体的,说不清楚的,能力你要具有,但是怎么实现,父类不管,由子类自己决定
抽象方法的定义格式:
修饰符 abstract 返回值类型 方法名称(参数列表...);
普通的成员方法,去掉{},添加abstract
含有抽象方法的类,必然是一个抽象类
抽象类的定义格式:
修饰符 abstract class 类名 {
//...
}
接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么 接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法 (JDK 9)。
注意:
1.子类/实现类,必须覆盖重写父类/接口中的所有抽象方法
2.父类和接口中有重名的抽象方法,只需要覆盖重写一次
3.多个接口中有重名的默认方法,实现类必须覆盖重写一次
4.父类中的普通方法和接口中的默认方法重名时,
优先使用父类的普通方法
5.多个接口中有重名的静态方法,也没有关系,因为静态方法只能使用接口名称调用
接口中的成员变量
都是常量,被public static final修饰,可以省略
接口中常量默认值认为是无效的,必须进行赋值
建议:
接口中常量的名字,全部大写,单词之间用_隔开
补充:
接口中的默认方法,实现类可以根据需求进行覆盖重写
不是强制性的
其他成员特点
接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
接口中,没有构造方法,不能创建对象。
调用接口中的3类方法(接口和实现类)
静态的内容:
1.要么使用类名调用
2.要么使用接口名调用
default:
1.类中的方法,不能有default关键字
2.接口中的带{}的方法,必须使用default关键字(除静态方法外)
- 抽象类要被子类继承,接口要被类实现。
- 接口可继承接口,并可多继承接口,但类只能单继承
- 抽象类可以有具体的方法 和属性, 接口只能有抽象方法和不可变常量
多态: 是指同一行为,具有多个不同表现形式。
前提【重点】
- 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】
java中的多态: 对象具有多态的特性
Student类是Person类的子类
学生是一个学生,学生是一个人
学生是一个学生:
Student s1 = new Student();//左侧Student类型,右侧Student对象,不是多态
学生是一个人:
Person p = new Student();// 左侧Person类型,右侧Student对象,是多态
多态调用方法:
方法跟着对象走
多态中成员的访问特点
成员变量:====了解====
编译看左边,运行看左边
编译和运行都看父类
成员方法:
编译看左边,运行看右边
编译看父类,运行看子类
多态的好处和弊端(可以结合多态中成员的访问特点来理解)
不使用多态:
好处: 可以调用子类特有方法
弊端: 扩展性差
使用多态:
好处: 扩展性强
弊端: 不能调用子类的特有行为
实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。
多态的引用类型转换
向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
为什么要转型
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥 有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子 类特有的方法,必须做向下转型。
3.注意:
向下转型时,如果转后类型和创建对象时的类型不一致,就会报出类型转换异常
类型转换异常:ClassCastException
怎么解决这个问题?
如果是Dog类型对象,转成Dog类型
如果是Cat类型对象,转成Cat类型
但是如何判断?
Animal a = new 谁();
谁?
java中提供了一个运算符?
boolean flag = 变量名 instanceof 数据类型;
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。
接口作为方法参数
ArrayList和LinkedList有共同的接口List
所以在定义方法时,使用接口List作为方法参数
传递接口的所有的实现类对象
接口作为方法的参数,可以提高复用性和扩展性
多态表示当同一个操作作用在不同对象时,会有不同的语义,从而产生不同的结果。3+4和“3”+“4”
Java的多态性可以概括成"一个接口,两种方法"分为两种编译时的多态和运行时的多态。编译时的多态主要是指方法的重载(overload),运行时的多态主要是指方法的覆盖(override),接口也是运行时的多态
运行时的多态的三种情况:
1、父类有方法,子类有覆盖方法:编译通过,执行子类方法。
2、父类有方法,子类没覆盖方法:编译通过,执行父类方法(子类继承)。
3、父类没方法,子类有方法:编译失败,无法执行。
方法带final、static、private时是编译时多态,因为可以直接确定调用哪个方法。
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构。比如,汽车类 Car 中包含发动机 类 Engine ,这时, Engine 就可以使用内部类来描述,定义在成员位置。
访问特点
内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须要建立内部类的对象。
创建内部类对象格式
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
调用一次接口Call中的sing方法
1.定义类,实现接口
2.覆盖重写接口中的抽象方法
3.创建实现类对象
4.实现类对象调用方法
为了调用接口中的sing方法,按照以前的方式,必须4步完成,太麻烦
java提供了一种语法格式,将上面的4步合成一步:匿名内部类对象
匿名内部类对象:
1.作用:
创建子类/实现类对象的一种快捷方式
2.格式:
new 抽象父类名/接口名() {
//覆盖重写所有的抽象方法
}
new Call(){
@Override
public void sing() {
System.out.println("鬼哭狼嚎...");
}
};
这可以用父类或者父接口接收,为了可以多次调用方法。
3.格式解释:
(1) new Call(): 创建对象,调用构造方法,构造方法是编译器指定的,我们不能指定
(2){}: 是Call接口的实现类的内容,但是实现类的名字不是我们指定的,是编译器指定的
4.前提
匿名内部类必须继承一个父类或者实现一个父接口。
文中部分知识引用自
https://blog.csdn.net/qq_43600458/article/details/102768325
https://blog.csdn.net/xianlvfan2224/article/details/102722298
https://blog.csdn.net/qq_36470686/article/details/83444483?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
https://blog.csdn.net/cillyb/article/details/81611090