🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 定义 创建java文件, 与类名相同 ~~~ public class 类名 { //可编写0至n个属性 数据类型 变量名1; 数据类型 变量名2; //可编写0至n个方法 修饰符 返回值类型 方法名(参数){ 执行语句; } } ~~~ # 使用 导包:我们将所有的类放到同一个文件夹下,可以避免导包。 创建对象:数据类型 变量名 = new 数据类型(); 访问属性:变量名.属性 # 局部变量和成员变量区别 区别一:定义的位置不同 定义在类中的变量是成员变量 定义在方法中或者{}语句里面的变量是局部变量 区别二:在内存中的位置不同 成员变量存储在对内存的对象中 局部变量存储在栈内存的方法中 区别三:声明周期不同 成员变量随着对象的出现而出现在堆中,随着对象的消失而从堆中消失 局部变量随着方法的运行而出现在栈中,随着方法的弹栈而消失 区别四:初始化不同 成员变量因为在堆内存中,所有默认的初始化值 局部变量没有默认的初始化值,必须手动的给其赋值才可以使用。 # 继承 当子父类中出现了同名成员变量时,在子类中若要访问父类中的成员变量,必须使用关键字super来完成。super用来表示当前对象中包含的父类对象空间的引用 父类私有的成员,子类不能继承,因为根本看不到 不能为了继承某个功能而随意进行继承操作, 必须要符合 is a 的关系 * 苹果 is a 水果 * 男人 is a 人 * 狗 is a 人 , 这种情况就不能继承了 # 覆盖 子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为override重写、复写或者覆盖 重写需要注意的细节问题: * 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。 * 写法上稍微注意:必须一模一样:方法的返回值类型 方法名 参数列表都要一样。 # 接口和抽象类的区别: * 相同点: - 都位于继承的顶端,用于被其他类实现或继承; - 都不能直接实例化对象; - 都包含抽象方法,其子类都必须覆写这些抽象方法; * 区别: - 抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法; - 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承) - 抽象类是这个事物中应该具备的你内容, 继承体系是一种 is..a关系 - 接口是这个事物中的额外内容,继承体系是一种 like..a关系 * 二者的选用: - 优先选用接口,尽量少用抽象类; - 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类; # 接口中的成员特点: 成员变量: 只能是final 修饰的常量 默认修饰符: public static final 构造方法: 无 成员方法: 只能是抽象方法 默认修饰符: public abstract # 多态 Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。 如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。 最终多态体现为父类引用变量可以指向子类对象。 多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。 在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。 ## 定义 多态的定义格式:就是父类的引用变量指向子类对象 父类类型 变量名 = new 子类类型(); 变量名.方法名(); * 普通类多态定义的格式 父类 变量名 = new 子类(); ~~~ 如: class Fu {} class Zi extends Fu {} //类的多态使用 Fu f = new Zi(); ~~~ * 抽象类多态定义的格式 抽象类 变量名 = new 抽象类子类(); ~~~ 如: abstract class Fu { public abstract void method(); } class Zi extends Fu { public void method(){ System.out.println(“重写父类抽象方法”); } } //类的多态使用 Fu fu= new Zi(); ~~~ * 接口多态定义的格式 接口 变量名 = new 接口实现类(); ~~~ 如: interface Fu { public abstract void method(); } class Zi implements Fu { public void method(){ System.out.println(“重写接口抽象方法”); } } //接口的多态使用 Fu fu = new Zi(); ~~~ ## 注意事项 * 同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。 ~~~ 如 Person p1 = new Student(); Person p2 = new Teacher(); p1.work(); //p1会调用Student类中重写的work方法 p2.work(); //p2会调用Teacher类中重写的work方法 ~~~ 当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法。 * 多态成员变量 **当子父类中出现同名的成员变量时,多态调用该变量时: 编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。 运行时期:也是调用引用型变量所属的类中的成员变量。 简单记:编译和运行都参考等号的左边。编译运行看左边。** * 多态成员方法 **编译时期:参考引用变量所属的类,如果没有类中没有调用的方法,编译失败。 运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。 简而言之:编译看左边,运行看右边。** ## 多态-转型 多态的转型分为向上转型与向下转型两种: * 向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。 使用格式: 父类类型 变量名 = new 子类类型(); 如:Person p = new Student(); * 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的! 使用格式: 子类类型 变量名 = (子类类型) 父类类型的变量; 如:Student stu = (Student) p; //变量p 实际上指向Student对象 ## 弊端 当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。向上转型的好处是隐藏了子类类型,提高了代码的扩展性。 但向上转型也有弊端,只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。看如下代码 我们来总结一下: * 什么时候使用向上转型: 当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。 ~~~ 如:Animal a = new Dog(); a.eat(); ~~~ * 什么时候使用向下转型 当要使用子类特有功能时,就需要使用向下转型。 ~~~ 如:Dog d = (Dog) a; //向下转型 d.lookHome();//调用狗类的lookHome方法 ~~~ * 向下转型的好处:可以使用子类特有功能。 * 弊端是:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。 如:if( !a instanceof Dog){…} # instanceof 我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。 使用格式: ~~~ boolean b = 对象 instanceof 数据类型; ~~~ 如 ~~~ Person p1 = new Student(); // 前提条件,学生类已经继承了人类 boolean flag = p1 instanceof Student; //flag结果为true boolean flag2 = p2 instanceof Teacher; //flag结果为false ~~~ # 构造方法 构造方法的体现: * 构造方法没有返回值类型。也不需要写返回值。因为它是为构建对象的,对象创建完,方法就执行结束。 * 构造方法名称必须和类型保持一致。 * 构造方法没有具体的返回值。 构造方法的代码体现: ~~~ class Person { // Person的成员属性age和name private int age; private String name; // Person的构造方法,拥有参数列表 Person(int a, String nm) { // 接受到创建对象时传递进来的值,将值赋给成员属性 age = a; name = nm; } } ~~~ 构造方法的细节: 1、一个类中可以有多个构造方法,多个构造方法是以重载的形式存在的 2、构造方法是可以被private修饰的,作用:其他程序无法创建该类的对象 ## this调用构造方法 构造方法之间的调用,可以通过this关键字来完成。 * 构造方法调用格式: ~~~ this(参数列表); ~~~ # super * 子父类中构造方法的调用 在创建子类对象时,父类的构造方法会先执行,因为子类中所有构造方法的第一行有默认的隐式super();语句。 格式: ~~~ 调用本类中的构造方法 this(实参列表); 调用父类中的空参数构造方法 super(); 调用父类中的有参数构造方法 super(实参列表); ~~~ 为什么子类对象创建都要访问父类中的构造方法?因为子类继承了父类的内容,所以创建对象时,必须要先看父类是如何对其内容进行初始化的 原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。这样,才可以使用父类中的内容。 当父类中没有空参数构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参数构造方法 如果子类的构造方法第一行写了this调用了本类其他构造方法,那么super调用父类的语句还有吗? 这时是没有的,因为this()或者super(),只能定义在构造方法的第一行,因为初始化动作要先执行。 父类构造方法中是否有隐式的super呢? 也是有的。记住:只要是构造方法默认第一行都是super(); 父类的父类是谁呢?super调用的到底是谁的构造方法呢? Java体系在设计,定义了一个所有对象的父类Object * 注意: 类中的构造方法默认第一行都有隐式的super()语句,在访问父类中的空参数构造方法。所以父类的构造方法既可以给自己的对象初始化,也可以给自己的子类对象初始化。 如果默认的隐式super()语句在父类中没有对应的构造方法,那么必须在构造方法中通过this或者super的形式明确要调用的构造方法 # 继承中的构造方法注意事项: 1. 如果我们手动给出了构造方法,编译器不会在给我们提供默认的空参数构造方法 如果我们没写任何的构造方法,编译器提供给我们一个空参数构造方法 2. 在构造方法中,默认的第一条语句为 super(); 它是用来访问父类中的空参数构造方法,进行父类成员的初始化操作 3. 当父类中没有空参数构造方法的时候,怎么办? a: 通过 super(参数) 访问父类有参数的构造方法 b: 通过 this(参数) 访问本类中其他构造方法 注意:[本类中的其他构造方法已经能够正常访问父类构造方法] 4.super(参数) 与 this(参数) 不能同时在构造方法中存在 # final * final修饰类不可以被继承,但是可以继承其他类 * final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖后可以加final。 * final修饰的变量称为常量,这些变量只能赋值一次。 * 引用类型的变量值为对象地址值,地址值不能更改,但是地址内的对象属性值可以修改。 ~~~ final Person p = new Person(); Person p2 = new Person(); p = p2; //final修饰的变量p,所记录的地址值不能改变 p.name = "小明";//可以更改p对象中name属性值 p不能为别的对象,而p对象中的name或age属性值可更改。 ~~~ * 修饰成员变量,需要在创建对象前赋值,否则报错。(当没有显式赋值时,多个构造方法的均需要为其赋值。) ~~~ class Demo { //直接赋值 final int m = 100; //final修饰的成员变量,需要在创建对象前赋值,否则报错。 final int n; public Demo(){ //可以在创建对象时所调用的构造方法中,为变量n赋值 n = 2016; } } ~~~ # static * 被static修饰的成员变量属于类,不属于这个类的某个对象。(也就是说,多个对象在访问或修改static修饰的成员变量时,其中一个对象将static成员变量值进行了修改,其他对象中的static成员变量值跟着改变,即多个对象共享同一个static成员变量) ~~~ class Demo { public static int num = 100; } class Test { public static void main(String[] args) { Demo d1 = new Demo(); Demo d2 = new Demo(); d1.num = 200; System.out.println(d1.num); //结果为200 System.out.println(d2.num); //结果为200 } } ~~~ * 被static修饰的成员可以并且建议通过类名直接访问。 访问静态成员的格式: ~~~ 类名.静态成员变量名 类名.静态成员方法名(参数) 对象名.静态成员变量名 ------不建议使用该方式,会出现警告 对象名.静态成员方法名(参数) ------不建议使用该方式,会出现警告 ~~~ ## 注意 * 静态内容是优先于对象存在,只能访问静态,不能使用this/super。静态修饰的内容存于静态区 ~~~ class Demo { //成员变量 public int num = 100; //静态方法 public static void method(){ //this.num; 不能使用this/super。 System.out.println(this.num); } } ~~~ * 同一个类中,静态成员只能访问静态成员 ~~~ class Demo { //成员变量 public int num = 100; //静态成员变量 public static int count = 200; //静态方法 public static void method(){ //System.out.println(num); 静态方法中,只能访问静态成员变量或静态成员方法 System.out.println(count); } } ~~~ * main方法为静态方法仅仅为程序执行入口,它不属于任何一个对象,可以定义在任意类中。 ## 定义静态常量 开发中,我们想在类中定义一个静态常量,通常使用public static final修饰的变量来完成定义。此时变量名用全部大写,多个单词使用下划线连接。 定义格式: ~~~ public static final 数据类型 变量名 = 值; ~~~ * 注意: 接口中的每个成员变量都默认使用public static final修饰。 所有接口中的成员变量已是静态常量,由于接口没有构造方法,所以必须显示赋值。可以直接用接口名访问。 ~~~ interface Inter { public static final int COUNT = 100; } 访问接口中的静态变量 Inter.COUNT ~~~ # 匿名对象 匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。 如:已经存在的类: ~~~ public class Person{ public void eat(){ System.out.println(); } } 创建一个普通对象 Person p = new Person(); 创建一个匿名对象 new Person(); ~~~ * 创建匿名对象直接使用,没有变量名。 ~~~ new Person().eat() //eat方法被一个没有名字的Person对象调用了。 ~~~ * 匿名对象在没有指定其引用变量时,只能使用一次。 ~~~ new Person().eat(); 创建一个匿名对象,调用eat方法 new Person().eat(); 想再次调用eat方法,重新创建了一个匿名对象 ~~~ # 内部类 * 什么是内部类 将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。其他类也称为外部类。 * 什么时候使用内部类 在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。 ~~~ class 汽车 { //外部类 class 发动机 { //内部类 } } ~~~ * 内部类的分类 内部类分为成员内部类与局部内部类。 我们定义内部类时,就是一个正常定义类的过程,同样包含各种修饰符、继承与实现关系等。在内部类中可以直接访问外部类的所有成员 ## 成员内部类 成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问 * 定义格式 ~~~ class 外部类 { 修饰符 class 内部类 { //其他代码 } } ~~~ * 访问方式 外部类名.内部类名 变量名 = new 外部类名().new 内部类名(); * 成员内部类代码演示 定义类 ~~~ class Body {//外部类,身体 private boolean life= true; //生命状态 public class Heart { //内部类,心脏 public void jump() { System.out.println("心脏噗通噗通的跳") System.out.println("生命状态" + life); //访问外部类成员变量 } } } ~~~ * 访问内部类 ~~~ public static void main(String[] args) { //创建内部类对象 Body.Heart bh = new Body().new Heart(); //调用内部类中的方法 bh.jump(); } ~~~ ## 局部内部类 局部内部类,定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问 * 定义格式 ~~~ class 外部类 { 修饰符 返回值类型 方法名(参数) { class 内部类 { //其他代码 } } } ~~~ * 访问方式 > 在外部类方法中,创建内部类对象,进行访问 * 局部内部类代码演示 定义类 ~~~ class Party {//外部类,聚会 public void puffBall(){// 吹气球方法 class Ball {// 内部类,气球 public void puff(){ System.out.println("气球膨胀了"); } } //创建内部类对象,调用puff方法 new Ball().puff(); } } ~~~ 访问内部类 ~~~ public static void main(String[] args) { //创建外部类对象 Party p = new Party(); //调用外部类中的puffBall方法 p.puffBall(); } ~~~ ## 匿名内部类概念 内部类是为了应对更为复杂的类间关系。查看源代码中会涉及到,而在日常业务中很难遇到,这里不做赘述。 最常用到的内部类就是匿名内部类,它是局部内部类的一种。 定义的匿名内部类有两个含义: 临时定义某一指定类型的子类 定义后即刻创建刚刚定义的这个子类的对象 * 定义匿名内部类的作用与格式 作用:匿名内部类是创建某个类型子类对象的快捷方式。 格式: ~~~ new 父类或接口(){ //进行方法重写 }; ~~~ * 代码演示 ~~~ //已经存在的父类: public abstract class Person{ public abstract void eat(); } //定义并创建该父类的子类对象,并用多态的方式赋值给父类引用变量 Person p = new Person(){ public void eat() { System.out.println(“我吃了”); } }; //调用eat方法 p.eat(); ~~~ 使用匿名对象的方式,将定义子类与创建子类对象两个步骤由一个格式一次完成,。虽然是两个步骤,但是两个步骤是连在一起完成的。 匿名内部类如果不定义变量引用,则也是匿名对象。代码如下: ~~~ new Person(){ public void eat() { System.out.println(“我吃了”); } }.eat(); ~~~ # 包 java的包,其实就是我们电脑系统中的文件夹,包里存放的是类文件。 当类文件很多的时候,通常我们会采用多个包进行存放管理他们,这种方式称为分包管理。 在项目中,我们将相同功能的类放到一个包中,方便管理。并且日常项目的分工也是以包作为边界。 类中声明的包必须与实际class文件所在的文件夹情况相一致,即类声明在a包下,则生成的.class文件必须在a文件夹下,否则,程序运行时会找不到类。 ## 声明格式 通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接 ~~~ 类中包的声明格式: package 包名.包名.包名…; ~~~ ## 包的访问 在访问类时,为了能够找到该类,必须使用含有包名的类全名(包名.类名)。 包名.包名….类名 ~~~ 如: java.util.Scanner java.util.Random ~~~ 带有包的类,创建对象格式:包名.类名 变量名 = new包名.类名(); * 前提:包的访问与访问权限密切相关,这里以一般情况来说,即类用public修饰的情况。 * 类的简化访问 当我们要使用一个类时,这个类与当前程序在同一个包中(即同一个文件夹中),或者这个类是java.lang包中的类时通常可以省略掉包名,直接使用该类。 * 当我们要使用的类,与当前程序不在同一个包中(即不同文件夹中),要访问的类必须用public修饰才可访问。 ## 导包 我们每次使用类时,都需要写很长的包名。很麻烦,我们可以通过import导包的方式来简化。 可以通过导包的方式使用该类,可以避免使用全类名编写(即,包类.类名)。 导包的格式: ~~~ import 包名.类名; ~~~ 当程序导入指定的包后,使用类时,就可以简化了。演示如下 ~~~ //导入包前的方式 //创建对象 java.util.Random r1 = new java.util.Random(); java.util.Random r2 = new java.util.Random(); java.util.Scanner sc1 = new java.util.Scanner(System.in); java.util.Scanner sc2 = new java.util.Scanner(System.in); //导入包后的方式 import java.util.Random; import java.util.Scanner; //创建对象 Random r1 = new Random(); Random r2 = new Random(); Scanner sc1 = new Scanner(System.in); Scanner sc2 = new Scanner(System.in); ~~~ * import导包代码书写的位置:在声明包package后,定义所有类class前,使用导包import包名.包名.类名; # 代码块 ## 局部代码块 局部代码块是定义在方法或语句中 特点: * 以”{}”划定的代码区域,此时只需要关注作用域的不同即可 * 方法和类都是以代码块的方式划定边界的 ## 构造代码块 构造代码块是定义在类中成员位置的代码块 特点: * 优先于构造方法执行,构造代码块用于执行所有对象均需要的初始化动作 * 每创建一个对象均会执行一次构造代码块。 ~~~ public class Person { private String name; private int age; //构造代码块 { System.out.println("构造代码块执行了"); } Person(){ System.out.println("Person无参数的构造函数执行"); } Person(int age){ this.age = age; System.out.println("Person(age)参数的构造函数执行"); } } class PersonDemo{ public static void main(String[] args) { Person p = new Person(); Person p1 = new Person(23); } } ~~~ ## 静态代码块 执行顺序:(优先级从高到低。)静态代码块>mian方法>构造代码块>构造方法。 静态代码块是定义在成员位置,使用static修饰的代码块。 特点: * 它优先于主方法执行、优先于构造代码块执行,当以任意形式第一次使用到该类时执行。 * 该类不管创建多少对象,静态代码块只执行一次。 * 可用于给静态变量赋值,用来给类进行初始化。 ~~~ public class Person { private String name; private int age; //静态代码块 static{ System.out.println("静态代码块执行了"); } } ~~~ # 修饰符 常用来修饰类、方法、变量的修饰符如下: * public 权限修饰符,公共访问, 类,方法,成员变量 * protected 权限修饰符,受保护访问, 方法,成员变量 * 默认什么也不写 也是一种权限修饰符,默认访问, 类,方法,成员变量 * private 权限修饰符,私有访问, 方法,成员变量 * static 静态修饰符 方法,成员变量 * final 最终修饰符 类,方法,成员变量,局部变量 * abstract 抽象修饰符 类 ,方法 我们编写程序时,权限修饰符一般放于所有修饰符之前,不同的权限修饰符不能同时使用; 同时,abstract与private不能同时使用; 同时,abstract与static不能同时使用; 同时,abstract与final不能同时使用。 * 修饰类能够使用的修饰符: 修饰类只能使用public、默认的、final、abstract关键字 使用最多的是 public关键字 * 修饰成员变量能够使用的修饰符: public : 公共的 protected : 受保护的 : 默认的 private :私有的 final : 最终的 static : 静态的 使用最多的是 private * 修饰构造方法能够使用的修饰符: public : 公共的 protected : 受保护的 : 默认的 private :私有的 使用最多的是 public * 修饰成员方法能够使用的修饰符: public : 公共的 protected : 受保护的 : 默认的 private :私有的 final : 最终的 static : 静态的 abstract : 抽象的 使用最多的是 public