文章目录
1、特性
抽象
抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。
抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
继承
继承是从已有类得到继承信息创建新类的过程。
提供继承信息的类被称为父类(超类基类] ;
得到继承信息的类被称为子类(派生类]。
继承让变化中的软件系统有了一定的延续性 ,同时继承也是封装程序中可变因素的重要手段。
封装
通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。
我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。
可以说,封装就是隐藏-切可隐藏的东西,只向外界提供最简单的编程接口。
多态
- 方法重载( overload )实现的是编译时的多态性(也称为前绑定)。
- 方法重写( override )实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西。
- 要实现多态需要做两件事:
- 1).方法重写
(子类继承父类并重写父类中已有的或抽象的方法) ; - 2).对象造型
用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
- 1).方法重写
2、overload与override
- overload
重载发生在同一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者者都不同)则视为重载。 - override
重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。根据不同的子类对象确定调用的那个方法。
3、static与final
-
Static
- 【修饰变量】: 静态变量随着类加载时被完成初始化,内存中只有一个,且JVM也只会为它分配一次内存,所有类共享静态变量。
- 【修饰方法】: 在类加载的时候就存在,不依赖任何实例; static方法必须实现,不能用abstract修饰。
- 【修饰代码块】: 在类加载完之后就会执行代码块中的内容。
- 父类静态代码块 ->子类静态代码块->父类非静态代码块->父类构造方法- >子类非静态代码块->子类构造方法
-
Final
- 【修饰变量】
-
编译期常量
类加载的过程完成初始化,编译后带入到任何计算式中。只能是基本类型。 -
运行时常量
基本数据类型或引用数据类型。引用不可变,但引用的对象内容可变。
-
- 【修饰方法】:不能被继承,不能被子类修改。
- 【修饰类】:不能被继承。
- 【修饰形参】:final形参不可变
- 【修饰变量】
4、内部类
非静态内部类
外部类里使用非静态内部类和平时使用其他类没什么不同
-
非静态内部类必须寄存在一个外部类对象里。
因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。 -
非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
非静态内部类对象单独属于外部类的某个对象。 -
非静态内部类不能有静态方法、静态属性和静态初始化块。
-
外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
-
成员变量访问要点:
- 内部类里方法的局部变量:变量名。
- 内部类属性:this.变量名。
- 外部类属性:外部类名.this.变量名。
-
内部类的访问:
- 外部类中定义内部类
- 外部类以外的地方使用非静态内部类
/**非静态内部类*/
class Outer {
private int age = 10;
public void show(){
System.out.println(age);//10
}
public int getAge(){
return age;//10
}
/**内部类Inner*/
public class Inner {
//内部类中可以声明与外部类同名的属性与方法
private int age = 20;
public void show(){
int age = 30;
System.out.println("内部类方法里的局部变量age:" + age);// 30
System.out.println("内部类的成员变量age:" + this.age);// 20
System.out.println("外部类的成员变量age:" + Outer.this.getAge());// 10
}
}
}
public class TestInnerClass {
public static void main(String[] args) {
//先创建外部类实例,然后使用该外部类实例创建内部类实例
Outer.Inner inner = new Outer().new Inner();
inner.show();
System.out.println("--------------------------");
Outer outer = new Outer();
Outer.Inner inn = outer.new Inner();
inn.show();
}
}
静态内部类
使用要点:
-
当一个静态内部类对象存在,并不一定存在对应的外部类对象。
因此,静态内部类的实例方法不能直接访问外部类的实例方法。
-
静态内部类看做外部类的一个静态成员。
因此,外部类的方法中可以通过:“静态内部类.名字”的方式访问静态内部类的静态成员,
通过 new 静态内部类()访问静态内部类的实例。
/*静态内部类*/
class Outer2{
//相当于外部类的一个静态成员
static class Inner2{
}
}
public class TestInnerClass {
public static void main(String[] args) {
//通过 new 外部类名.内部类名() 来创建内部类对象
Outer2.Inner2 inner2 =new Outer2.Inner2();
}
}
匿名内部类
适合那种只需要使用一次的类。比如:键盘监听操作等等。
-
匿名内部类 没有访问修饰符。
-
所以当所在方法的形参需要被匿名内部类使用,那么这个 形参就必须为final
-
匿名内部类 没有构造方法 。因为它连名字都没有那又何来构造方法呢。
-
如何访问在其外面定义的变量?
当所在方法的形参需要被匿名内部类使用, 那么这个形参就必须为final -
匿名内部类是省略了【实现类/子类名称】
匿名对象省略了【对象名称】
强调:匿名内部类和匿名对象名称不是一回事
匿名内部类的定义格式:
接口名称 对象名 = new 接口名称(){
//覆盖重写所有抽象方法
};
局部内部类
定义在方法内部的,作用域只限于本方法
/*局部内部类*/
class Outer4 {
public void show() {
//作用域仅限于该方法
class Inner4 {
public void fun() {
System.out.println("helloworld");
}
}
new Inner4().fun();
}
}
public class TestInnerClass {
public static void main(String[] args) {
// 局部内部类的调用
new Outer4().show();
}
}
5、接口
[访问修饰符] interface 接口名 [extends 父接口1, 父接口2…] {
常量定义;
方法定义;
}
-
子类通过implements来实现接口中的规范。
-
接口不能创建实例,但是可用于声明引用变量类型。
-
一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
-
JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
-
JDK1.8后,接口中包含普通的静态方法。
6、模板方法与回调
简单点说明就是:
A类中调用B类中的C方法,然后B类中的C方法中反过来调用A类中的D方法,那么D这个方法就叫回调方法。
回调的具体过程如下:
- Class A实现接口CallBack —— 背景1
- class A中包含class B的引用 ——背景2
- class B有一个参数为CallBack的方法C ——背景3
- 前三条是我们的准备条件,接下来A的对象调用B的方法C
- 然后class B就可以在C方法中调用A的方法D
示例
/** 回调接口 */
interface CallBack {
/** 小高知道答案后告诉小刘时需要调用的方法,即回调方法
* @param result 是问题的答案 */
public void answer(String result);
}
/** 小刘类:实现了回调接口CallBack(背景一) */
class Liu implements CallBack {
/** 包含小高对象的引用 (背景二) */
private Gao gao;
public Liu(Gao gao){ this.gao = gao; }
/** 小刘通过这个方法去问小高
* @param question 小刘问的问题“学习Java选哪家机构呢?” */
public void askQuestion(String question){
//小刘问小高问题
gao.execute(Liu.this, question);
}
/** 小高知道答案后调用此方法告诉小刘 */
@Override
public void answer(String result) {
System.out.println("小高告诉小刘的答案是:" + result);
}
}
/** 小高类 */
class Gao {
/** 相当于class B有一个参数为CallBack的方法C(背景三) */
public void execute(CallBack callBack, String question){
System.out.println("小刘问的问题是:" + question);
//模拟小高挂点后先办自己的事情花了很长时间
try { Thread.sleep(10000); }
catch (InterruptedException e) { e.printStackTrace(); }
//小高办完自己的事情后想到了答案
String result = "学Java";
//小高打电话把答案告诉小刘,相当于class B 反过来调用class A 的D方法
callBack.answer(result);
}
}
public class Test {
public static void main(String[] args) {
Gao gao= new Gao();
Liu liu = new Liu(gao);
//小刘问问题
liu.askQuestion("学习哪种语言");
}
}
7、常用
instanceof 运算符
对象 instanceof 类
this与super
this
public class TestThis {
int a, b, c;
TestThis() {
System.out.println("正要初始化一个Hello对象");
}
TestThis(int a, int b) {
// TestThis(); //这样是无法调用构造方法的!
this(); // 调用无参的构造方法,并且必须位于第一行!
this.a = a;
this.b = b;
}
TestThis(int a, int b, int c) {
this(a, b); // 调用带参的构造方法,并且必须位于第一行!
this.c = c;
}
public static void main(String[] args) {
TestThis hi = new TestThis(2, 3);
}
}
super
用法与this
类似,只是super
调用的是父类的方法、属性、构造器
==与equal
-
==
:比较双方是否相同。
如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。 -
equal
:默认就是比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。
数组拷贝
从src
的第srcpos
个开始复制长度为length
的对象到dest
,从dest
的第destpos
开始存
static void arraycopy(
object src, //
int srcpos, //
object dest, //
int destpos, //
int length //
)
for each
- 仅适用于遍历,循环是不能更改值
自动装箱、拆箱
- 自动装箱
基本类型的数据处于需要对象的环境中时,会自动转为“对象”
Integer i = 100;//自动装箱
//相当于编译器自动为您作以下的语法编译:
Integer i = Integer.valueOf(100);//调用的是valueOf(100),而不是new Integer(100)
- 自动拆箱
每当需要一个值时,对象会自动转成基本数据类型,没必要再去显式调用intValue()、doubleValue()等转型方法。
Integer i = 100;
int j = i;//自动拆箱
//相当于编译器自动为您作以下的语法编译:
int j = i.intValue();
-
包装类的缓存问题
整型、char类型所对应的包装类,在自动装箱时,对于-128~127之间的值会进行缓存处理,其目的是提高效率。
缓存处理的原理为
如果数据在-128~127这个区间,那么在类加载时就已经为该区间的每个数值创建了对象,并将这256个对象存放到一个名为cache的数组中。
每当自动装箱过程发生时(或者手动调用valueOf()时),就会先判断数据是否在该区间,
如果在则直接获取数组中对应的包装类对象的引用,
如果不在该区间,则会通过new调用包装类的构造方法来创建对象。
String类和常量池
- 暂未学习
对String不变性的理解
参考链接:String s=new String(“abc“)创建了几个对象?
- String不变性
-
String
类是被final
进行修饰的,不能被继承。
且为private
访问权限,所以只有SUN的工程师可以拿到这个字符数组。
而SUN的工程师在所有方法中都没有改变该字符数组的值。 -
在用
+
号链接字符串的时候会创建新的字符串。 -
通过使用
+
符号来串联字符串的时候实际上底层会转成通过StringBuilder
实例的append()
方法来实现 -
String s = new String("Hello world" );
可能创建两个对象也可能创建一个对象。- 如果静态区中有"Hello world"字符串常量对象的话,则仅仅在堆中创建一个对象。
- 如果静态区中没有"Hello world"对象,则堆上和静态区中都需要创建对象。
-
String、StringBuffer、StringBuilder区别
- 三者区别
- 都是
final
类,都不允许被继承; String
长度是不可变的,
StringBuffer
.StringBuilder
长度是可变的;StringBuffer
是线程安全的,StringBuilder
不是线程安全的,但它们两个中的所有方法都是相同的。
StringBuffer
在StringBuilder
的方法之.上添加了synchronized
修饰,保证线程安全。StringBuilder
比StringBuffer
拥有 更好的性能。- 如果一个
String
类型 的字符串,在编译时就可以确定是一个字符串常量,则编译完成之后,字符串会自动拼接成一个常量。
此时String
的連度比StringBuffer
和StringBuilder
的性能好的多。
- 都是
重写hashcode、toString的相关问题
- String有重写Object的hashcode和toString?
- 若重写equals不重写hashcode会出现什么问题?
-
String
重写了Object
的hashcode
和toString
方法。 -
重写
equals
时有必要重写hashcode
方法,以维护hashcode
方法的常规协定,该协定声明相对等的两个对象必须有相同的hashcode
object1.euqal(object2)为true时,object1.hashCode0 == object2.hashCode0
为true
object1.hashCode() == object2hashCode() 为false时, object1.euqal(object2)必定为false
object1.hashCode() == object2.hashCode()为true时,但object1.euqal(object2)不一定为true -
重写
equals
时不重写重写hashcode
方法出现的问题在存储散列集合时(如Set类) ,如果
原对象.equals(新对象)
,但没有对hashCode重写,即两个对象拥有不同的hashCode ,则在集合中将会存储两个值相同的对象,从而导致混淆。因此在重写equals方法时,必须重写hashCode方法。
-