1.面向对象基础
1.1 对象内存解析
堆:中存放的是new出来的结构比如(数组,对象)加载在堆空间中,对象的非static属性也是加载在堆空间中。
方法区:类的加载信息、常量池、静态域。
栈:存放的是局部变量。
1.2 局部变量与属性
1. 相同点
1.1 定义变量的格式:数据类型: 变量名 = 变量值
1.2 先声明,后使用
1.3 变量都有对应的作用域
2. 不同点
2.1 在类中声明的位置不同
属性:直接定义在{}中
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.2 关于权限修饰符的不同
属性:可以指定修饰符public private protected 缺省
局部变量:不可以
2.3 默认初始化
属性:根据类型不同都有初始化值,引用类型(数组、类、接口)为null值
局部变量:没有默认的初始化值,意味着我们一定要显式赋值
2.4 在内存中加载的位置
属性:加载到堆空间中(非static)
局部变量:加载到栈空间
1.3 重载
定义:指的是在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或参数类型不同即可
“两同一不同”:同一个类、相同方法名
参数列表不同、参数个数不同、参数类型不同
如何判断:跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系
1.4 方法参数的值传递机制
Java里面的参数传递方法只有一种机制:值传递。即将实际参数值的副本传入方法内,而参数本身不受影响
—->形参是基本类型,将实参基本数据类型变量的数据值传给了形参
—->形参是引用类型,将实参引用数据类型变量的地址值传给了形参
1.5 面向对象特征之一:封装与隐藏
为什么需要封装:用手机、洗衣机、开车没必要知道原理,只需要会用就好了
程序追求:高内聚,低耦合
高内聚:类的内部数据细节自己完成,不允许外部干涉
低耦合:对外暴露少量的方法使用
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装的思想
四种访问修饰符:
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
缺省 | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
对于class的修饰可以有public和default
>public类可以在任意地方访问
>default类只可以被同一个包内部的类访问
4种权限都可以用来修饰类的内部结构:属性,方法,构造器,内部类
封装性的体现:
1.将类的属性私有化,同时提供相应的set,get方法;
2.不对外暴露私有的方法;
3.单例模式(将构造器私有化);
4.如果不希望类在包外被调用,可以将类设置为缺省的
1.6 类的构造器
特征:与类具有相同的名称,没有返回类型,不能被static、final、synchronized、abstract、native修饰、不能有return返回值
作用:创建对象,给对象进行初始化
说明:1)如果没有显式的提供构造器,系统会提供一个空参构造器;
2)一个类定义多个构造器,彼此构成重载;
3)显式的提供构造器后系统不会再提供默认构造器,一个类至少有一个构造器。
1.7 拓展知识:JavaBean
JavaBean是一种Java语言写的可重用组件;
所谓的JavaBean是指符合下列标准的Java类:
类是公共的
有一个无参的公共构造器
有属性,且有对应的get set方法
1.8 package与import
package:为了更好管理项目中的类提供Package概念,位于源文件的首行,同一个包下不能命名同名的接口,类。
import:1)在源文件中显式的使用import结构导入指定包下的类,接口;
2)声明在包的声明和类的声明之间;
3)如果需要导入多个接口,则并列写出即可;
4)如果在源文件中,使用了不同包名下的同名的类,则有一个类必须要以全类名的形式显示;
5)import static:导入指定的类或接口中的静态结构。
1.9 面向对象的特征之二:继承性
一、继承性的好处
1.1 减少了代码的冗余,提高了代码的复用性;
1.2 便于功能的拓展;
1.3 为之后多态性的使用,提供了前提。
二、继承性的格式:class A extends B { }
A: 子类、派生类、subclass
B: 父类、超类、superclass
2.1 体现:一旦子类A继承父类B之后,子类A中就获取了父类B中声明的所有属性和方法。
特别地:父类中声明为private的属性和方法,子类继承父类之后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
2.2 子类继承父类之后,还可以声明自己特有的属性和方法,实现功能的拓展。子类和父类的关系,不同于子集和集合的关系。
extends:延展,拓展
三、Java中关于继承性的规定:
1、一个类可以被多个子类继承;
2、Java中类的单继承性,一个类中只能有一个父类;
3、子父类是相对的概念;
4、子类直接继承的父类,称为直接父类。间接继承的父类称为:间接父类;
5、子类继承父类以后、就获取了直接父类和所有间接父类的声明的属性和方法。
四、Object类
1、所有的类都直接或间接继承于java.lang.Object中的类;
2、所有的类都可以用Object类中的功能。
五、方法的重写(Override/overwrite)
1、重写:子类继承父类之后,可以对父类中同名同参数的方法,进行覆盖操作。
2、应用:重写以后,当创建子类对象后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写的方法。
3、重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型 {
方法体//
}
约定俗成:子类中叫重写的方法、父类中叫被重写的方法。
(1) 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同;
(2) 子类中重写的方法修饰符不小于父类被重写的方法的修饰符,**特别地**,**子类不能重写父类中声明为private权限的方法**。
(3) 返回值类型:
1. 父类中被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void;
2. 父类中被重写的方法的返回值类型是A类型,则子类重写的方法可以是A类或A类的子类;
3. 父类中被重写的方法的返回值类型是基本数据类型(比如double),则子类重写的方法的返回值类型必须 是相同类型;
4. 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
子父类中同名同参数的方法要么都声明为static的,要么都声明为非static的(考虑重写)。
4、 super调用构造器
4.1 我们可以在子类的构造器中显式的使用**super(形参列表)**的方式。调用父类中声明的指定的构造器;
4.2 **super(形参列表)**的使用,必须声明在子类构造器的首行;
4.3 我们在类的构造器中,针对于**this(形参列表)**或**super(形参列表)**只能二选一,不能同时出现;
4.4 在构造器的首行,没有显示的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器;
4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器。
1.10 面向对象特征之三:多态性
多态性,是面向对象中最重要的概念,在java中的体现。
对象的多态性:父类的引用指向子类的对象。可以直接应用在抽象类和接口上。对象的多态性只能适用于方法,不适用于属性。
java引用变量有两个类型:编译时类型和运行时类型。编译时类型有声明该变量时使用的类型所决定,运行时类型由实际赋给该变量的对象决定。简称:编译时看左边,运行时看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性。我们在编译期只能调用父类中声明的方法,但在运行期我们实际执行的是子类重写的方法。有了对象的多态性后,内存中实际上加载了子类特有的属性和方法的。但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。如何才能调用子类的属性和方法?
—-向下转型,使用强制类型转换符
使用强转时,可能出现ClassCastException
instance of关键字的使用
a instanceof A:判断对象a是否是类A的实例,如果是则返回true,如果不是,返回false.
Person person = new man();
Man man = (Man) person;
//内存中来讲,new man()加载了子类的所有的属性和方法将其赋给父类,在强转给子类是没有问题的。
Person person = new Person();
Man man = (Man) person;
//这里new Person()只加载了父类的属性和方法,并没有记载子类的属性和方法,故强转给子类会出现问题。
多态情况下:”看左边”:看的是父类的引用(父类中不具备子类特有的方法);
“看右边”:看的子类的对象(实际运行的是子类中重写父类的方法);
虚拟方法调用:
- 正常的方法调用:
Person person = new Person();
person.getInfo();
Student stu = new Student();
stu.getInfo();
- 虚拟方法调用(多态情况下):
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同的子类的对象,动态调用属于子类的该方法,这样的方法调用在编译期是无法确定的。Person e = new Student();
e.getInfo();//实际调用的是Student类中的getInfo()方法
- 编译时类型和运行时类型
编译时e为person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。———-动态绑定
小结:方法的重载和重写
1.从定义上看:….
2.从编译时和运行时的角度看:
重载是指允许存在多个同名方法,而这些方法的参数不同,编译器根据方法的不同的参数表,对同名方法的名称做修饰。对于编译器而言。这些同名方法就成了不同的方法,它们的调用地址在编译期就绑定了。java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”。
而对于多态而言,只要等到方法调用的那一刻,运行器才能确定所要调用的具体方法,这称为“动态绑定”。
子类对象实例化的全过程:
从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性和方法;
创建子类的对象,在堆空间中就会加载所有父类中声明的属性。
从过程中来说:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,知道调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类结构,所以才可以看到内存中父类的结构,子类对象才可以考虑进行调用。明确:虽然创建子类对象时调用了父类的构造器,但始终创建的一个对象,那就是new的子类对象。
经典习题:
class Base1 {
public void add(int a,int... arr) {
System.out.println("base1");
}
}
class Sub1 extends Base1 {
public void add(int a,int[] arr) {
System.out.println("sub_1");
}
public void add(int a,int b,int c) {
System.out.println("sub_2");
}
}
class Test {
public static void main(String[] args) {
Base1 base = new Sub1();
base.add(1,2,3);//sub_1。因为多态性的原因执行重子类重写的方法。
Sub s = (Sub1) base;
s.add(1,2,3);//sub_2。参数列表明确,优先选择。
}
}
1.11 ==与equals()
1、==运算符
1.1 如果比较的基本数据类型,则比较的两个变量保存的值是否相等;
1.2 如果比较的是引用数据类型,则比较的是两个对象的地址值是否相同,即两个引用是否指向同一个对象实体。
注: ==符号使用时,必须保证符号左右两边的变量类型一致。
2、equals()方法
2.1 是一个方法并非运算符;
2.2 只能用于引用类型;
2.3 Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
object类中==与equals()方法实际上是一样的,比较的是地址值。
2.4 像String、Date、File、包装类等重写了equals()方法,重写后比较的不是地址,而是实体内容。
1.12 static关键字
1.可以用来修饰属性,方法,代码块,内部类
2.static关键字修饰的变量叫做静态变量,所有对象共享这一份静态变量,随着类加载而加载。存在于方法的静态域中。
3.static关键字修饰的方法叫做静态方法,静态方法可以调用静态方法和属性,不可以调用实例方法。实例方法谁都可以调用。
4.注意:静态方法中,不可以用this关键字,super关键字。
1.13 单例设计模式
1.单例模式指的是:在整个软件系统中,对某个类只能存在一个对象实例。前提条件是将构造器的访问权限设置为private,并且提供一个静态方法返回类内部创建的对象实例。
2.饿汉式:
public class Singleton {
public static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return Singleton;
}
}
3.懒汉式:
public class Singleton {
public static Singleton singleton = null;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null)
singleton = new Singleton();
return Singleton;
}
}
4.区别:饿汉式:好处:线程安全,坏处:加载时间长
懒汉式:好处:延迟对象的创建,坏处:线程不安全
1.14 代码块
1.作用:用来初始化类、对象。可以用static修饰
2.静态代码块:随着类加载而执行。并且只执行一次。如果有多个静态代码块,按照写的顺序执行。
3.普通代码块:随着对象创建而执行,每创建一次对象都会执行一次。
1.15 final修饰符
1.final可以用来修饰的结构:类、方法、变量
2.final修饰类的时候表示该类不可以被继承比如String类,final修饰方法的时候表示该类不可以被重写,final修饰变量的时候,表示该变量不可以被再赋值。
1.16 abstract关键字
1.用来修饰类和方法。不能用来修饰属性,构造器等结构。
2.abstract修饰类的时候表示该类不能实例化,抽象类中一定有构造器其便于子类实例化时进行调用。
3.abstract修饰方法:
>抽象方法只有方法的声明,没有方法体。
>包含抽象方法的类一定是抽象类,抽象类不一定有抽象方法。
>子类在重写所有的父类抽象类的方法后才可以实例化,否则还是抽象类需要用abstract修饰。
1.17 接口Interface
1.接口和类是并列的结构。
2.JDK7以前:接口中只能定义全局常量和抽象方法。 >全局常量:public static final的,但是书写时可以省略不写。
>抽象方法:public abstract
JDK8:除了定义全局常量和抽象方法以外,还可以定义静态方法、默认方法。
3.接口中不能定义构造器。意味着接口不可以实例化。
4.开发中通常用类来实现(implements)接口,如果实现接口中所有抽象方法,则类可以实例化。如果只实现了部分抽象方法,则还是抽象类。
5.java中可以实现多接口,接口与接口是继承关系,并且可以多继承。
1.18 main方法
1.public static void main(String args[])。
2.main方法需要虚拟机调用。java虚拟机需要调用类的main()方法。所以该方法的访问权限必须是public。
3.java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static。
4.该方法接收String类型的数组参数,该数组中保存执行java命令时所传递给所运行的类的参数。
5.java 执行的程序 参数1 参数2 参数3
注意:在main方法中,我们可以直接调用main方法所在类的静态方法或静态属性,但是不能直接访问类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
1.19 内部类
分类:定义在外部类的局部位置(比如方法内)
1)局部内部类(有类名);2)匿名内部类(没有类名)
定义在外部类的成员位置上:
1)成员内部类(没有static修饰);2)静态内部类(有static修饰)
1.局部内部类
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1)可以直接访问外部类的所有成员,包含私有的。
2)不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不可以用访问修饰符的,但可以用final修饰,因为局部变量可以用final修饰。
3)作用域:仅仅定义在它的方法或代码块中。
4)局部内部类—->访问外部类的成员(访问方式:直接访问)。
5)外部类—->访问局部内部类的成员(访问方式:先创建对象,在访问。必须在作用域内)。
6)外部其他类不能访问局部内部类。相当于一个局部变量。
7)如果外部类和局部内部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
原因:外部类.this本质就是外部类的对象,即哪个对象调用了m1,外部类.this就是哪个对象。
注意:1)局部内部类定义在方法中/代码块;
2)作用域在方法中/代码块中;
3)本质上仍然是一个类。
2.匿名内部类
1)本质是类,内部类,该类没有名字,同时还是一个对象。
2)匿名内部类定义在外部类的局部位置,比如方法中,并且没有类名。
3)语法:new 类或接口(参数列表){
**类体**
**};**
class IATest {
public void method1() {
IA ia =new IA() {
@Override
public void cry() {
System.out.println("tiger is crying");
}
};
ia.cry();
System.out.println(ia.getClass());
}
/*底层: 会分配类名 IATest$1
class IATest$1 implements IA {
public void cry() {
System.out.println("tiger is crying");
}
}
*/
4)注意匿名内部类可以编译类型和运行类型不一样。
5)语法比较奇特,匿名内部类既是一个类的定义,同时本身还是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,可以直接调用方法,new Thread {}.start();
3.成员内部类
1)定义位置,外部类的成员位置,并且没有static修饰。
2)可以直接访问外部类的成员,包含私有的。
3)可以添加访问修饰符,因为它的地位就是一个成员。
4)外部类访问内部类,创建对象在访问。
4.静态内部类
1)定义在成员位置,有static修饰。
2)可以添加访问修饰符。地位就是一个成员。
3)可以访问外部类的所有静态成员包含私有的,但不能直接访问非静态成员。
4)外部类的成员和静态内部类的成员重名时,内部类访问的话,遵循就近原则,如果访问外部类的成员,则可以使用类名.成员访问。