Java_面向对象
面向对象
面向对象是基于面向过程的编程思想
面向过程:强调的是每一个功能的步骤
面向对象:强调的是面向对象,然后由对象去调用功能
面向对象的思想特点
1、是一种更符合我们思想习惯的思想
2、可以将复杂的事情简单化
3、将我们从执行者变成了指挥者
面向对象开发
就是不断的创建对象,使用对象,指挥对象做事情
面向对象设计
就是在管理和维护对象之间的关系
面向对象特征
封装(encapsulation)
继承(inheritance)
多态(polymorphism)
类:是一组相关的属性和行为的集合,是一个抽象的概念
对象:是该类事物的具体表现形式,具体存在的个体
创建对象的格式:
类名 对象名 = new 类名();
/* 在一个java文件中写两个类:一个基本的类,一个测试类。 注意:文件名称和测试类名称一致。 如何使用呢? 创建对象使用。 如何创建对象呢? 格式:类名 对象名 = new 类名(); 如何使用成员变量呢? 对象名.变量名 如何使用成员方法呢? 对象名.方法名(...) */ //这是学生类 class Student { //姓名 String name; //null //年龄 int age; //0 //地址 String address; //null //学习 public void study() { System.out.println("学生爱学习"); } //吃饭 public void eat() { System.out.println("学习饿了,要吃饭"); } //睡觉 public void sleep() { System.out.println("学习累了,要睡觉"); } } //这是学生测试类 class StudentDemo { public static void main(String[] args) { //类名 对象名 = new 类名(); Student s = new Student(); //输出成员变量值 //System.out.println(s.name); //System.out.println(s.age); //System.out.println(s.address); //改进写法 System.out.println(s.name+"---"+s.age+"---"+s.address); //给成员变量赋值 s.name = "林青霞"; s.age = 27; s.address = "北京"; //赋值后的输出 System.out.println(s.name+"---"+s.age+"---"+s.address); //调用方法 s.study(); s.eat(); s.sleep(); } }
构造方法
构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。
格式:
public 类名称(参数类型 参数名称){
方法体;
}
注意事项:
1、构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样。
2、构造方法不要返回值类型,连void都不写。
3、构造方法不能return一个具体的返回值。
4、如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做
public Student() {}
5、一旦写了至少一个构造方法,那么编译器将不再赠送。
6、构造方法也是可以进行重载的(方法名相同, 参数列表不同)。
package com.xdl; public class Student { private String name; private int age; public Student() { System.out.println("构造方法被执行了!"); } public Student(String name, int age) { System.out.println("全参构造方法被执行了!"); this.name = name; this.age = age; } } /*------------------------* package com.xdl; public class TestPerson { public static void main(String[] args) { Student stu1 = new Student(); //无参构造 Student stu2 = new Student("xdl", 18); //全参构造 } }
定义一个标准类(Java Bean)
一个标准的类通常包括下面四个组成部分:
1、所有的成员变量都要使用private关键字修饰
2、为一个成员变量编写一对Getter/Setter方法
3、编写一个无参数的构造方法
4、编写一个全参数的构造方法
技巧:编写一个类实现两个私有成员变量后,可以通过idle工具生成代码(code-->Generate)
package com.xdl; public class Student { private String name; //姓名 private int age; //年龄 public Student() { // 无参数构造方法 } public Student(String name, int age) { //全参数构造方法 this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
static关键字
静态属性
如果一个成员变量使用了static关键字,那么这个变量不再属于对象自己,而是属于所在的类。多个对象共享同一份数据。
package com.xdl.demostatic; public class Student { private int id; private String name; private int age; static String room; private static int idCounter = 0; //学号计数器,每当new了一个新对象的时候,计数器++ public Student() { Student.idCounter++; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Student(String name, int age) { this.name = name; this.age = age; this.id = ++Student.idCounter; } public static String getRoom() { return room; } public static void setRoom(String room) { Student.room = room; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } /********************/ package com.xdl.demostatic; public class StaticField { public static void main(String[] args) { Student.setRoom("001"); // 或Student.room = "001" Student stu1 = new Student("张三", 15); System.out.println("姓名:" + stu1.getName() + "年龄:" + stu1.getAge() + "学号:" + Student.getRoom() + "班级:" + stu1.getId()); Student stu2 = new Student("李四", 1); System.out.println("姓名:" + stu2.getName() + "年龄:" + stu2.getAge() + "学号:" + Student.getRoom() + "班级:" + stu2.getId()); } }
静态方法:
1、一旦使用static修饰成员方法,那么这就成为了静态方法。静态方法不属于对象,而是属于类。
2、如果没有static关键字,那么必须首先创建对象,然后通过对象才能使用它。
3、如果有了static关键字,那么不需要创建对象,直接就能通过类名称进行调用
总结:
无论是成员属性,还是成员方法,如果有了static,都推荐使用类名称进行调用。
注意事项:
1、静态不能直接访问非静态
原因:因为在内存中是先有的静态内容,后有的非静态内容
2、静态方法中不能用this
原因:this代表当前对象,通过谁调用的方法,谁就是当前对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demostatic; public class MethodStatic { public void method() { System.out.println("这是一个成员方法"); } public static void staticMethod() { System.out.println("这是一个静态方法"); } } /***********************/ package com.xdl.demostatic; public class MethodStaticDemo { public static void main(String[] args) { MethodStatic obj = new MethodStatic(); //首先创建对象 // 然后才能使用没有static关键字的内容 obj.method(); // 对于静态方法来说,可以通过对象名进行调用,也可以执行通过类名称来调用 obj.staticMethod(); //正确,不推荐,这种写法在编译之后也会被javac编译成为"类名.静态方法名" MethodStatic.staticMethod(); //正确,推荐 method(); // 对于本类当中的静态方法,可以省略类名称, MethodStaticDemo.method(); // 完全等效与上面的语句 } public static void method() { System.out.println("自己的方法"); } }
静态代码块
格式:
public class 类名称 {
static {
//静态代码块内容
}
}
特点:
1、当第一次用到本类时,静态代码块执行唯一的一次。
2、静态内容总是优先于非静态,所以静态代码块比构造方法先执行。
典型用途:
用来一次性地对静态成员变量进行赋值。
1 package com.xdl.demostatic; 2 3 public class Person { 4 static { 5 System.out.println("静态代码块被执行。"); 6 } 7 8 public Person() { 9 System.out.println("构造方法被执行。"); 10 } 11 } 12 /***************************/ 13 package com.xdl.demostatic; 14 15 16 public class ChunkStatic { 17 public static void main(String[] args) { 18 Person one = new Person(); 19 Person two = new Person(); 20 /** 21 * 静态代码块被执行。 22 * 构造方法被执行。 23 * 构造方法被执行。 24 */ 25 } 26 }
面向对象三大特性:封装、继承、多态
封装:
1、成员方法本身就是封装的体现。
2、私有的成员属性(private)也是封装的体现。
package com.xdl; public class Person { private String name; private int age; private boolean male; // 尽量使用set+成员属性首字母大写 public void setAge(int num) { if (num>=0 && num <=100) { age = num; return; } System.out.println("年龄应该在0~100之间"); } public int getAge() { return age; } public void setMale(boolean m){ male = m; } // 如果返回的是boolean类型, 建议使用is+属性首字母大写 public boolean isMale() { return male; } } /*---------------------------------------------*/ package com.xdl; public class TestPerson { public static void main(String[] args) { Person person = new Person(); person.setAge(10); System.out.println(person.getAge()); person.setMale(true); System.out.println(person.isMale()); } }
继承
- 继承是多态的前提,,如果没有继承,就没有多态。
- 继承主要解决【共性抽取】
- 父类也被称为超类、基类
- 子类也被称为派生类
- 在继承关系中,“子类就是一个父类”,也就是说子类可以被当做父类看待;例如:父类是员工,子类是讲师,那么讲师就是一个员工。
特点:
- java继承是单继承, 一个类同时只能继承一个父类(平级的)。
- 一个父类可以被多个子类继承。
- 一个子类可以继承一个父类,这个父类还可以继承其它父类。
继承的格式(extends)
public class 子类名 extends 父类名 {
//....
}
继承中成员变量的访问特点
- 通过子类对象直接访问成员变量
- 等号左边是谁,就优先用谁,没有则向上找。
- 通过成员方法间接访问成员变量
- 该方法属于谁,就优先用谁,没有则向上找。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo01; class Fu { int numFu = 10; int num = 100; public void methodFu() { // 使用的是本类当中的num, 不会向下找子类num System.out.println(num); } } class Zi extends Fu { int numZi = 20; int num = 200; public void methodZi() { // 优先找本类中的num,如果没有再去找父类中的num System.out.println(num); } } public class ExtendFieldDemo { public static void main(String[] args) { Fu fu = new Fu(); // 创建父类 System.out.println(fu.numFu); //10 只有父类的东西,没有任何子类的内容 System.out.println(fu.num); // 100 Zi zi = new Zi(); // 创建子类 System.out.println(zi.numZi); //20 System.out.println(zi.numFu); //10 首先找子类中的numFu, 没有则去父类中找 System.out.println(zi.num); //200 zi.methodZi(); // 200 zi.methodFu(); // 100 这个方法是在父类中定义的 } }
区分子类方法中三种重名的变量
- 局部变量:直接使用
- 本类的成员变量:this.变量名
- 父类的成员变量:super.变量名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo01; class Fu { int num = 10; } class Zi extends Fu { int num = 20; public void method() { int num = 30; System.out.println(num); // 30 局部变量 System.out.println(this.num); // 20 本类成员变量 System.out.println(super.num); //10 父类成员变量 } } public class ExtendFieldDemo { public static void main(String[] args) { Zi zi = new Zi(); zi.method(); } }
继承中成员方法的访问特点
创建的对象是谁,就优先用谁,如果没有则向上找。
总结:
无论是成员方法还是成员变量,如果没有都向上找父类,绝对不会向下找子类的。
重写(override)
概念:在继承关系中,方法名称一样、参数列表也一样。也称为覆盖、覆写,为了和重载区分开,经常称为覆盖重写。
特点:创建的是子类对象,则优先使用子类方法。
注意事项:
- 必须保证父子类中的方法名称一样、参数列表也一样。
- @Override修改在方法前面,用来检查是不是有效的正确覆盖重写,这个注解不是必须的。
- 子类方法的返回值必须【小于等于】父类方法的返回值。
- 提示:java.lang.Object类是所有类的公共最高父类,java.lang.Strin就是Object的子类
- 子类方法的权限必须【大于等于】父类方法的权限修饰符。
- public > protected > (default) > private, default是什么都不写。
package com.xdl.demo01; class Fu { public Object method(String name) { System.out.println("父类的方法被调用。" + name); return null; } } class Zi extends Fu { @Override public String method(String name) { // 方法的权限必须大于等于父类方法的权限, String类是Object的子类 System.out.println("子类的方法被调用。" + name); return name; } } public class OverrideDemo { public static void main(String[] args) { Zi zi = new Zi(); zi.method("xdl"); } }
继承中父子类构造方法的访问特点
- 子类构造方法当中有一个默认隐含的super()调用, 所以一定是先调用的父类的构造方法,然后执行的子类构造。
- 子类构造可以通过super关键字来调用父类重载构造。
- super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造
- 只有子类构造方法才能调用父类构造方法,(子类super()必须写在构造方法里面)
- 子类必须调用父类构造方法,不写则赠送super(),写了则用写的指定的super(),super()只能有一个,还必须是第一个语句。
package com.xdl.demo02; class Fu { public Fu() { System.out.println("父类的无参构造方法被调用。"); } public Fu(String name) { System.out.println("父类的有参构造方法被调用。"); } } class Zi extends Fu { public Zi() { // super(); // 默认包含一个隐藏调用 System.out.println("子类的无参构造方法被调用。"); //super(); //java: 对super的调用必须是构造器中的第一个语句 Call to 'super()' must be first statement in constructor body } public Zi(String name) { // super(); // 默认包含一个隐藏调用 super(name); // 调用父类的有参构造方法 System.out.println("子类的有参构造方法被调用。"); } public void method() { // super(); java: 对super的调用必须是构造器中的第一个语句 } } public class ConstructDemo { public static void main(String[] args) { new Zi(); new Zi("xdl"); } }
super关键字的三种用法
- 在子类的成员方法中,访问父类的成员变量
- 在子类的成员方法中,访问父类的成员方法
- 在子类的构造方法中,访问父类的构造方法
this关键字的三种用法
- 在本类的成员方法中,访问本类的成员变量
- 在本类的成员方法中,访问本类的另一个成员方法
- 在本类的构造方法中,访问本类的另外一个构造方法。
- this(...)调用也必须是构造方法的第一个语句,唯一一个。
- super和this两种构造调用,不能同时使用。
package com.xdl.demo03; class Fu { public Fu() { System.out.println("父类的无参构造方法被调用"); } } class Zi extends Fu { private int age; public void method() { int age = 20; System.out.println("age: " + this.age); // 访问成员变量 this.method2(); // method2() } public void method2() { System.out.println("method2方法被执行。"); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Zi() { this(30); // 子类的有参构造方法被调用, 父类的构造方法将不会被调用 System.out.println("子类的无参构造方法被调用"); // this(); // Call to 'this()' must be first statement in constructor body } public Zi(int age) { System.out.println("子类的有参构造方法被调用"); this.age = age; } } public class ThisDemo { public static void main(String[] args) { Zi zi = new Zi(); zi.setAge(10); zi.method(); } }
抽象(abstract)
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类必须是抽象类,在class之前加上abstract即可。
如何使用抽象类和抽象方法:
- 不能直接创建(new)抽象类对象。
- 必须用一个子类来继承抽象类。
- 子类必须覆盖重写抽象类中所有的抽象方法,除非子类也是抽象类。
- 覆盖重写(实现): 子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
- 创建子类对象进行使用。
- 抽象类中不一定有抽象方法,但是抽象方法一定要在抽象类中。
- 抽象类中可以有构造方法。
- 非抽象子类必须实现所有父类的所有抽象方法。
package com.xdl.demo04; abstract class Animal { public abstract void eat();// Abstract method in non-abstract class, Abstract methods cannot have a body // 这是一个普通的成员方法 public void method() { System.out.println("普通方法被调用,子类不是必须重写该方法"); } public Animal() { System.out.println("抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员使用的。"); } } class Cat extends Animal {//// Class 'Cat' must either be declared abstract or implement abstract method 'eat()' in 'Animal' @Override public void eat() { System.out.println("猫吃鱼"); } } abstract class Dog extends Animal { public abstract void sleep(); } class JinMao extends Dog { // 非抽象子类必须实现所有父类的所有抽象方法 @Override public void sleep() { } @Override public void eat() { } } public class AbstractDemo { public static void main(String[] args) { //new Animal(); // 'Animal' is abstract; cannot be instantiated Cat cat = new Cat(); cat.eat(); cat.method(); } }
接口(interface)
概念:接口就是多个类的公共规范, 接口是一种引用数据类型,最重要的内容就是其中的抽象法方法。
格式:
public interface 接口名称 {
//接口内容
}
说明:换成关键字interface之后,编译生成的字节码文件任然是:.java-->.class
接口中的5个内容:
如果是java7那么接口中可以包含的内容有
1、常量
2、抽象方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo02; /** * 在任何版本的java中,接口都能定义抽象方法 * 注意事项: * 1、接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract * 2、这两个关键字修饰符,可以选择性地省略. * 3、方法的三要素,可以随意定义。 * 使用步骤: * 1、接口不能直接使用,必须有一个实现类来实现该接口 * 格式: * public class 实现类名称 implements 接口名称 { * //... * } * 2、接口的实现类必须覆盖重写接口中所有的抽象方法。 * 3、创建实现类的对象,进行使用 * 在任何版本的java中,接口都能定义"成员变量",但是必须使用public static final 三个关键字进行修饰。 * 从效果上看,这其实就是接口的常量。 * 格式: * public static final 数据类型 常量名称 = 数据值; * 备注: * 一旦使用final关键字进行修饰,说明不可变。 * 注意事项: * 1、接口当中的常量,可以省略public static final,注意:不写也照样是这样的。 * 2、接口当中的常量,必须进行赋值,不能不赋值。 * 3、接口当中的常量,使用完全大写的字母,用下划线进行分割。(推荐使用命名规则) * 使用方式: * 1、接口.名称 * 2、实现类.名称 * 3、实现类对象.名称 */ interface MyInterface { public static final int NUM1 = 10; int NUM2 = 10; public abstract void methodAbs1(); void methodAbs2(); } class Inter implements MyInterface { @Override public void methodAbs1() { System.out.println("methodAbs1"); } @Override public void methodAbs2() { System.out.println("methodAbs2"); } } public class MyInterfaceAbs { public static void main(String[] args) { System.out.println(MyInterface.NUM1); Inter inter = new Inter(); inter.methodAbs1(); System.out.println(inter.NUM1); System.out.println(Inter.NUM1); } }
java8可以额外包含:
3、默认方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo02; /** * 从java8开始,接口允许定义默认方法。 * 格式: * public default 返回值类型 方法名称(参数列表) { * // 方法体 * } * 备注: * 接口当中的默认方法,可以解决接口升级的问题。在接口中添加了新的默认方法,不会应该原始的接口的实现类 * 1、接口的默认方法,可以通过接口的实现类对象直接调用 * 2、接口的默认方法,也可以通过接口实现类进行覆盖重写 * */ interface DefaultInter { // 抽象方法 public abstract可以省略 public abstract void method1(); //新添加一个默认方法 public 可以省略 public default void method2() { System.out.println("新添加了method2方法"); }; } class D1 implements DefaultInter { @Override public void method1() { System.out.println("D1"); } @Override public void method2() { System.out.println("重写了接口的默认方法"); } } class D2 implements DefaultInter { @Override public void method1() { System.out.println("D2"); } } public class InterfaceDefault { public static void main(String[] args) { D1 d1 = new D1(); D2 d2 = new D2(); d1.method1(); d2.method1(); d1.method2(); d2.method2(); } }
4、静态方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo02; /** * 从java8开始,接口允许定义静态方法。 * 格式: * public static 返回值类型 方法名称(参数列表) { * // 方法体 * } * 注意: * 不能通过接口实现类或实现类的对象来调用接口的静态方法,原因:一个类可以实现多个接口会产生冲突。 * 直接通过接口直接调用: * 接口名称.静态方法名(参数); */ interface StaticInter { // public可以省略 public static void methodStatic() { System.out.println("这是一个接口的静态方法!"); } } class S1 implements StaticInter { } public class InterfaceStatic { public static void main(String[] args) { StaticInter.methodStatic(); // 接口直接调用静态方法 S1 s1 = new S1(); // S1.methodStatic(); //错误写法 // s1.methodStatic(); //错误写法 } }
java9可以额外包含:
5、私有方法:
用来解决接口中多个方法中重复代码的问题,并且这个方法不能被接口实现类使用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo05; /** * 问题描述: * 我们需要一个公共的方法,用来解决两个默认方法之间重复代码的问题, * 但是这个公共方法不应该让是现实使用,应该是私有化的。 * 解决方案: * 从Java9开始接口中允许定义私有方法, 私有方法只能在接口中使用 * 1、普通私有方法:解决多个默认方法之间重复代码问题 * private 返回值类型 方法名称(参数列表) { * // 方法体 * } * 2、静态私有方法:解决多个静态方法之间重复代码问题 * private static 返回值类型 方法名称(参数列表){ * //方法体 * } */ interface Inter { public default void method1() { System.out.println("method1"); methodCommon(); } public default void method2() { System.out.println("method2"); methodCommon(); } public static void method3() { System.out.println("method3"); //methodCommon(); // Non-static method 'methodCommon()' cannot be referenced from a static context methodStaticCommon(); } private void methodCommon(){ System.out.println("AAA"); System.out.println("BBB"); System.out.println("CCC"); } private static void methodStaticCommon(){ System.out.println("AAA"); System.out.println("BBB"); System.out.println("CCC"); } } class InterClass implements Inter { } public class PrivateMethod { public static void main(String[] args) { InterClass ic = new InterClass(); ic.method1(); Inter.method3(); // ic.methodCommon();// 无法调用 } }
接口使用的注意事项
- 接口是没有静态代码块或构造方法的
- 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口
- 格式:public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB { // 覆盖重写所有抽象方法}
- 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可
- 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类
- 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
- 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,优先用父类当中的方法。
- 类与类之间是单继承的,直接父类只有一个;类与接口之间是多实现的,一个可以实现多个接口;接口与接口之间是多继承的。
- 多个父接口中的抽象方法如果重复,没关系;多个父接口中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且必须带着default关键字。
多态
extends继承或者implements实现,是多态性的前提。
代码中多态的体现,就是父类的引用指向子类的对象
格式:
父类名称 对象名 = new 子类名称();
或者:
接口名称 对象名 = new 实现类名称();
多态中成员变量和成员方法的使用特点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package demo01; /** * 多态中成员变量访问特点 * 1、直接访问:通过对象直接成员变量 * 等号左边是谁,就优先用谁,没有则向上找。 * 2、间接访问:通过成员方法访问 * 该方法属于谁,就优先用谁,没有则向上找。 * 多态中成员方法的访问特点 * 1、new的是谁,就有限用谁,没有这向上找 * 口诀: * 成员变量:编译看左边,运行看左边 * 成员方法:编译看左边,运行看右边。 */ class Fu { int age = 10; public void showAge() { System.out.println(age); } public void method() { System.out.println("父类方法"); } public void methodFu() { System.out.println("父类特有方法"); } } class Zi extends Fu { int age = 20; @Override public void showAge() { System.out.println(age); } public void method() { System.out.println("子类方法"); } public void methodZi() { System.out.println("子类特有方法"); } } public class Multi { public static void main(String[] args) { // 多态:父类的引用指向子类的对象 Fu zi = new Zi(); System.out.println(zi.age); // 10 通过对象直接访问成员变量 // 通过成员方法间接访问,子类如果覆盖重写了父类方法则为20, 如果没有覆盖重写则为10 zi.showAge();//20 zi.method(); zi.methodFu(); //编译看左边,左边是Fu,Fu中没有methodZi方法所有编译报错 // zi.methodZi(); // 错误写法 } }
优点:无论右边new的时候换成那个子类对象,等号左边调用方法都不会改变。
向上转型
对象的向上转型,其实就是多态写法。
格式:父类名称 对象名 = new 子类名称();
含义:右侧创建一个子类对象,把它当作父类来看待使用。
注意事项:
向上转型一定是安全的,从小范围转向了大范围
类似于:double num = 100; int -->double,自动类型转换
向下转型
对象的向下转型,其实是一个【还原的动作】。
格式:子类名称 对象名 = (子类名称) 父类对象;
含义:将父类对象,【还原】成为本来的子类对象。
Animal animal = new Cat(); // 本来是猫,向上转型成为动物
Cat cat = (Cat) animal;//本来是猫,已经被当成动物了,还原回来成为本来的猫。
注意事项:
a. 必须保证对象本来创建的时候,就是猫,才能向下转型成为猫
b.如果对象创建的时候不是猫,现在非要向下转型成为猫,就会报错.(ClassCastException)
类似于:int num = (int) 10.0; //可以 int num = (int) 10.5;//错误,精度损失精度
instanceof: 用于判断某个对象是否是某个类的对象(包括父类、接口)
对象名 instanceof 类名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package demo02; abstract class Animal{ abstract void eat(); } class Cat extends Animal{ @Override void eat() { System.out.println("猫吃鱼"); } void catchMouse(){ System.out.println("猫捉老鼠"); } } public class Switch { public static void main(String[] args) { Animal animal = new Cat(); // 本来是猫,向上转型成为动物 System.out.println(animal instanceof Cat); //true System.out.println(animal instanceof Animal); //true animal.eat(); // cat.catchMouse() // 错误写法 Cat cat = (Cat) animal; // 动物上下转型还原成为猫 cat.catchMouse(); System.out.println(cat instanceof Animal); //true System.out.println(cat instanceof Cat); //true } }
final关键字
final关键字代表最终、不可改变的。
常见四种用法:
- 可以用来修饰一个类,表示这个类不能继承
- 可以用来修饰一个方法
- 可以用来修改一个局部变量
- 可以用来修饰一个成员变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo07; /** * 当final关键字用来修饰一个类的时候,格式: * [修饰符] final class 类名称 { * //... * } * 含义:当前这个类不能有任何的子类,不能不继承 * 注意:一个类如果是final的, 那么其中所有的成员方法无法进行覆盖重写,不能有抽象方法 * 当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就不能被覆盖重写。格式: * [修饰符] final 返回值类型 方法名称(参数列表){ * //方法体 * } * 注意事项: * 对于类、方法来说,abstract关键字和final关键字不能同时使用。 * 对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变的。 * 1、由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。 * 2、对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。 * 3、必须保证类当中所有重载的构造方法, 都最终会对final的成员变量进行赋值。 */ final class Fu { public void method() { System.out.println("方法执行!"); } } public class FinalDemo { private final int age = 18; private final String name; //所有的构造方法都要对name进行初始化 public FinalDemo(String name) { this.name = name; } public FinalDemo() { this.name = "xdl"; } public static void main(String[] args) { // 一旦使用final关键字修饰局部变量,那么这个变量就不能进行更改, 一次赋值,终生不变。 // 对于基本类型来说,不可变说的是变量中的数据不可变。 // 对于引用类型来说, 不可变说的是变量当中的地址值不可改变。 final int num1 = 10; // num1 = 100; // Cannot assign a value to final variable 'num1' final int num2; //局部变量没有初始化 // System.out.println(num2); // Variable 'num2' might not have been initialized num2 = 100; //正确写法,只要保证唯一一次赋值即可 } }
四个权限修饰比较
是否可以访问 | public | protected | 什么都不写 | private |
同类 | true | true | true | true |
同包/通过类对象方法 | true | true | true | false |
不同包子类 | true | true | false | false |
不同包非子类 | true | false | false | false |
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo06; public class C1 { public int age = 10; public void method() { // age 权限修饰符为private、不写、protected、public是同类都可以访问 System.out.println("age"); } } /***********************/ package com.xdl.demo06; public class C2 { public void method () { //age修饰符为public、protected、不写时可以访问,private不能访问 System.out.println(new C1().age); } } /**************************/ package com.xdl.demo06.other; import com.xdl.demo06.C1; public class C3 extends C1 { public void method1() { // 当age的修饰符为public、protected时可以访问;不写或者private时不能访问 System.out.println(age); System.out.println(super.age); } } /****************************************/ package com.xdl.demo06.other; import com.xdl.demo06.C1; public class C4 { public void method() { // 当age的修饰符为public时可以访问;当为protected、不写、private时不能访问 System.out.println(new C1().age); } }
内部类(Inner Class)
概念: 如果一个类定义在另一个类的内部,这个类就是内部类
分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
成员内部类
修饰符 class 类名称 {
修饰符 class 内部类名称 {
//...
}
}
注意:内用外随意访问;外用内需要内部类对象。
使用成员内部类的两种方式
- 间接访问:在外部类的方法中,使用内部类;然后main只是调用外部类的方法
- 直接使用:公式【外部类.内部类 对象名 = new 外部类().new 内部类();】
如果出现重名现象,那么格式是:外部类名称.this.外部类成员变量名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo08; public class Body { //外部类 int num = 10; // 外部类的成员变量 public class Heart { //内部类 int num = 20; // 内部类的成员变量 public void beat() { int num = 30; // 内部类的局部变量 System.out.println("内部类方法"); System.out.println("可以直接使用外部类的成员变量" + name); System.out.println(num); //30 局部变量就近原则 System.out.println(this.num);//20内部类的成员变量 System.out.println(Body.this.num);//10外部类的成员变量 } } public void methBody() { System.out.println("外部类方法"); // 通过内部类间接使用内部类方法 Heart heart = new Heart(); heart.beat(); } private String name = "xdl"; public String getName() { return name; } public void setName(String name) { this.name = name; } } /******************************************/ package com.xdl.demo08; public class DemoInner { public static void main(String[] args) { Body body = new Body(); body.methBody(); Body.Heart heart = new Body().new Heart(); heart.beat(); //直接使用内部类方法 } }
局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类
只有当前所属的方法才能使用它,出了这个方法外就不能使用了
定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
// ...
}
}
}
局部内部类,如果希望访问所在方法的局部变量,那个这个局部变量必须是【有效final的】。
备注: 从Java 8+开始,只要局部变量事实不变, 那么final关键字可以省略。
原因:
- new 出来的对象在堆内存中。
- 局部变量是跟着方法走的,在栈内存中。
- 方法运行结束之手,立刻出栈,局部变量就会立刻消失
- 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.xdl.demo09; class Outer { public void methOuter() { int num = 20; // 内部类方法使用了外部类局部变量,则这个变量默认会变为常量 class Inner { // 局部内部类, 不能写任何修饰符 public void methodInner() { // Variable 'num' is accessed from within inner class, needs to be final or effectively final System.out.println(num); } } // 只能在外部类方法中使用内部类 Inner inner = new Inner(); inner.methodInner(); } } class DemoOuter { public static void main(String[] args) { Outer outer = new Outer(); outer.methOuter(); } }
定义一个类的时候,权限修饰符规则:
- 外部类: public / (default),如果使用public修饰还需要与.java文件同名(Class 'Outer' is public, should be declared in a file named 'Outer.java')
- 为什么不能用private修饰:private表示同类中可以方法,外部类不存在同类。
- 为什么不能用protected修饰:protected比defalut多一个不同包子类可以访问,那么不同包的类首先要继承这个类,如果使用protected修饰则无法使用又怎么继承呢。
- 成员内部类: public/protected/(default)/private
- 局部内部类: 什么都不能写
匿名内部类
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。
匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
//覆盖重写所有抽象方法
};
对格式"new 接口名称() {...}"进行解析:
- new代表创建对象的动作
- 接口名称就是匿名内部类需要实现的那个接口
- {...}这才是匿名内部类的内容
注意事项:
- 匿名内部类,在创建对象的时候,只能使用唯一一次。如果希望多次创建对象,而且类的内容一样的话,那么久必须使用单独定义的实现类
- 匿名对象,在调用方法的时候,只能调用唯一一次。如果希望同一个对象,调用多次方法,那么必须对对象起一个名字。
- 匿名内部类是省略了实现类/子类名称,但是匿名对象是省略了对象名称