面向对象编程
this
指代当前对象,那个对象调用方法就是哪个对象,只能用在方法中,在方法中访问成员变量之前默认有个this
。- 定义在类中方法外的变量称作成员变量,在方法中的变量成为局部变量,局部变量和成员变量是可以同名的,因为存储位置不同。
- 成员变量和局部变量同名时,this不能省略,一般情况下是可以省忽略的。
THIS用法:
this.成员变量名
---------------------访问成员变量;this()
---------------------------------调用构造方法;
向上造型
1):超类型的引用指向派生类的对象,既定义超类型的引用可以指向派生类的对象。
2):能点出来什么看引用的类型。
访问过程:超类不能访问派生类的,派生类可以访问超类的
样例:
package javapratice;
public class SuperDemo{
public static void main(String[] args){
Person[] per = new Person[4];
per[0] = new Student("zhangsan", 16, "LF", "dafeiji");
per[1] = new Student("lisi", 18, "JMS", "asd");
per[2] = new Teacher("DDD", 55, "XSD", "54385412");
per[3] = new Teacher("BXB", 999, "JN", "5214525");
}
}
class Person{
String name;
int age;
String address;
Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
void sayHi(){
System.out.println(name + "," + age + "," + address);
}
}
class Student extends Person{
String hobby;
Student(String name, int age, String address, String hobby){
super(name, age, address);
this.hobby = hobby;
}
}
class Teacher extends Person{
String teacherID;
Teacher(String name, int age, String address, String teacherID){
super(name, age, address);
this.teacherID = teacherID;
}
}
有很多个派生类要继承超类时,每个派生类如果要创建一个派生类的数组并且要遍历派生类的数组从而要调用超类中的某个方法时,可以使用向上造型实现代码的简化,将所有的派生类数组都装到超类类型的数组中,数组的每个元素为超类类型,调用派生类的构造方法。
重写
1):发生在父子类中,方法名称相同,参数列表相同,方法体不同
在上述的代码中,遍历过程sayHi
只能访问超类的属性(超类不允许访问子类的属性),而不能加上子类的特有属性,而子类的特有属性以储存在内存中,为了将这种子类特有的属性展示出来,利用重写:
package javapratice;
public class SuperDemo{
public static void main(String[] args){
Person[] per = new Person[4];
per[0] = new Student("zhangsan", 16, "LF", "dafeiji");
per[1] = new Student("lisi", 18, "JMS", "asd");
per[2] = new Teacher("DDD", 55, "XSD", "54385412");
per[3] = new Teacher("BXB", 999, "JN", "5214525");
}
}
class Person{
String name;
int age;
String address;
Person(String name, int age, String address){
this.name = name;
this.age = age;
this.address = address;
}
void sayHi(){
System.out.println(name + "," + age + "," + address);
}
}
class Student extends Person{
String hobby;
Student(String name, int age, String address, String hobby){
super(name, age, address);
this.hobby = hobby;
}
void sayHi(){
System.out.println(name + "," + age + "," + address + "," + hobby);// 重写sayHi
}
}
class Teacher extends Person{
String teacherID;
Teacher(String name, int age, String address, String teacherID){
super(name, age, address);
this.teacherID = teacherID;
}
void sayHi(){
System.out.println(name + "," + age + "," + address + "," + teacherID);// 重写sayHi
}
}
zhangsan,16,LF,dafeiji
lisi,18,JMS,asd
DDD,55,XSD,54385412
BXB,999,JN,5214525
当超类的方法无法满足派生类的需求时,用重写。
2):重写调用看对象,与引用类型无关,只看对象。
换句话说:当派生类的重写方法被调用时(无论是通过子类调用还是父类引用调用)运行的都是子类重写之后的版本
Student zs = new Student("Zhangsan", 16, "JMS", "DAFEIJI");//调用student类
Person zss = new Student("Zhangsan", 5169, "JN", "SDS");//调用student类
调用重写之前的方法:super().方法名
用来调用超类的方法,既重写之前的方法
3):遵循两同两小一大:
3.1):两同:
3.1.1)方法名称相同
3.1.2)参数列表相同
3.2):两小:
3.2.1)派生类方法的返回值类型必须小于等于超类方法的
1)void时,必须相等
2)基本类型时,必须相等
3)引用类型时,小于或等于
3.2.2)派生类方法抛出的异常小于或等于超类方法的
3.3)一大:
3.3.1)派生类方法的访问权限大于或等于超类方法的
测试代码:
class Aoo{
void test1(){}
double test2(){}
Aoo test3(){}
Boo test4(){}
}
//引用类型,超类大,派生类小
class Boo extends Aoo{
void test1(){} //正确
void test1(){} //编译错误,void时必须保持一致
int test2(){} //编译错误,虽然double大于int,但是都属于基本数据类型,基本数据类型必须保持相同
Boo test3(){} //编译正确,引用类型方法,符合派生类小于等于超类
Aoo test4(){} //编译错误,派生类大于超类,不符合重写规则,所以编译错误
}
通常情况下的重写,不用对返回值类型改动。
重载与重写的区别:
1)重写:
1.1)重写发生在父子类中,方法名称相同,参数列表相同,方法体不同。
2.2)遵循”运行期绑定“,看对象的类型调用方法。
2)重载:
2.1)发生在一个类中,方法名称相同,参数列表不同,方法体不同。
2.2)遵循“编译器绑定”,看参数的个数和参数的类型来绑定方法
package javapratice;
public class chongxie {
public static void main(String[] args){
Goo goo = new Goo();
Eoo o = new Eoo();
goo.test(o); //发生重载,参数o的类型为Eoo,所以直接调用参数类型为Eoo的方法,输出超类参数。
}
}
class Goo{ //重载
void test(Eoo o){
System.out.println("超类参数");
o.show(); //运行到这里发生重写,看o的对象,为Eoo,输出超类show。
}
void test(Foo o){
System.out.println("派生类参数");
o.show();
}
}
//重写
class Eoo{
void show(){
System.out.println("超类show");
}
}
class Foo extends Eoo{
void show(){
System.out.println("派生类show");
}
}
static静态变量
成员变量:
1):实例变量:没有static修饰,属于对象的,储存在堆中,有几个对象就有几份,通过对象名点来访问。
2):静态变量:有static修饰,属于类,储存在方法区中,只有一份,通过类名点来访问。
静态方法
1):static修饰,属于类,储存在方法区中,只有一份,通过类名点来访问
class Moo{
int a;
static int b;
void show(){
System.out.println(a); // this.a
System.out.println(b); // Moo.b
}
static void test(){
System.out.println(a); //编译错误.
System.out.println(b);
}
}
解释:实例变量和实例方法都属于实例对象,在实例方法中访问实例变量时传递this代表对象,可以用this.访问,静态方法和静态变量都 属于类,和对象无关,所以在静态方法中不传递this,既没有对象,实例变量属于实例,必须通过对象访问,需要用this.访问,综 上, 无法在静态方法中访问实例变量,只能访问静态变量。
静态块:
1)属于类的,在类被加载期间自动执行,由于类只被加载一次,所以静态块只执行一次。
2)何时用:常常用于加载/初始化静态资源。
class Foo{
static {
System.out.println("Load this Foo");
}
public Foo(){
System.out.println("Foo()");
}
}
STATIC FINAL常量:
1):编译器在编译时,将常量直接替换为具体的值,效率高。
2):静态变量在使用时,先将类加载到方法区中,静态变量储存到方法区中,从方法区中获取变量的值。
内存管理
- 堆:new出来的对象(包括实例变量)
- 栈:局部变量(包括方法的参数)
- 方法区:.class字节码文件(包括方法、静态变量)
package tets1;
public class StaticDemo {
public static void main(String[] args){
staticDemo2 o1 = new staticDemo2();
o1.print();
staticDemo2 o2 = new staticDemo2();
o2.print();
staticDemo2 o3 = new staticDemo2();
o3.print();
staticDemo2 o4 = new staticDemo2();
o4.print();
}
}
class staticDemo2{
int a;
static int b;
staticDemo2(){
a ++ ;
b ++ ;
}
void print(){
System.out.println("a=" + a + ", b = " + b);
}
}
面向对象编程的思考:
1):访问不同类中的static方法,由于static从属于类,和对象无关,在另一个类中调用该方法只需要“ 类名 . 方法名” 即可实现访问。
2):访问不同类中的实例方法:实例方法属于对象,只能通过对象进行访问,new一个对象进行:对象名 . 方法名访问。
ABSTRACT 抽象类与抽象方法:
-
抽象方法:
1.1)由abstract修饰;
1.2)只有方法定义,没有具体的实现(连{ }都没有)
-
抽象类:
2.1)由abstract修饰;
2.2)包含抽象方法的类必须是抽象类;
2.3)抽象类不能被实例化;
2.4)抽象类需要被继承,派生类:
2.4.1)重写抽象类中所有的抽象方法
-
抽象类的意义:
1)封装固有的属性和行为,抽到超类中---------------------抽共性
2)为所有派生类提供统一的类型------------------------------向上造型
3)可以包含抽象方法,为所有的派生类提供统一的入口,派生类的具体实现不同,但入口是一致的。
import java.util.Scanner;
public class asdasd{
public static void main(String[] args){
Test2[] tests = new Test2[3];
for(int i = 0; i < 3; i ++ ){
tests[i] = new Test2();
tests[i].step();
}
}
}
abstract class Test1{
Test1(){}
abstract void step();
}
class Test2 extends Test1{
Test2(){}
void step(){
System.out.println("Hello World!");
}
}
引用类型数组
- 数组类型的内存图:
数组中的每个元素作为int[]
对象的成员变量储存在堆中。
引用类型变量里面储存的为地址。
-
注解:给引用数据类型赋值一定要new一个对象。
Student[] stus = new Student[3]; //创建student数组对象 //stus数组中每一个元素都为引用数据类型,为他们赋值要new对象 stus[0] = new Student("zhangsan", 25, "LF"); // 调用构造方法,创建student对象
接口
1)是一种数据类型
2)有interface定义
3)只能包含常量和抽象方法
4)访问权限默认为public
5)接口不能被实例化,单独存在没有意义
6)接口需要被实现,在实现类中必须要重写所有抽象方法
7 )一个类可以实现多个接口,用逗号隔开
8)一个接口可以继承多个接口
多态
1. 同一类型的引用指向不同的对象时有不同的实现 ---------------------行为的多态
2. 同一个对象被造型为不同的类型时,有不同的功能-------------------对象的多态
向上造型(自动类型转换):
1. 超类型的引用指向派生类的对象
2. 能造型成为的类型:超类 + 所实现的接口
3. 能点出来什么看引用的类型
强制类型转换:
基本类型之间强制转换,一定正确,但可能溢出或丢失精度
引用类型之间强制转换,可能会失败报错,抛出异常
强制类型转换,成功的条件只有如下两种:
1)引用所指向的对象就是该类型
2)引用所指向的对象,实现了该接口或继承了该类
在强制转换时若不符合以上两种条件,那么发生ClassCastException
类型转换异常,建议在类型转换之前先通过
instanceof
来判断引用的对象是否是该类型
个接口,用逗号隔开
8)一个接口可以继承多个接口
多态
1. 同一类型的引用指向不同的对象时有不同的实现 ---------------------行为的多态
2. 同一个对象被造型为不同的类型时,有不同的功能-------------------对象的多态
向上造型(自动类型转换):
1. 超类型的引用指向派生类的对象
2. 能造型成为的类型:超类 + 所实现的接口
3. 能点出来什么看引用的类型
强制类型转换:
基本类型之间强制转换,一定正确,但可能溢出或丢失精度
引用类型之间强制转换,可能会失败报错,抛出异常
强制类型转换,成功的条件只有如下两种:
1)引用所指向的对象就是该类型
2)引用所指向的对象,实现了该接口或继承了该类
在强制转换时若不符合以上两种条件,那么发生ClassCastException
类型转换异常,建议在类型转换之前先通过
instanceof
来判断引用的对象是否是该类型