类的高级概念
推荐一个好的方法:在java中,学习新的东西的时候,我们要站在复用与分离的角度去看待。把问题简单化。
访问修饰符
- public 公开的
- protected 受保护的
- 默认 没有访问修饰符
- private 私有的
其各自的权限范围为
如下图:
封装
大多数时间,属性全部用private。常量用public static。
get/set方法的封装体现
1、可以只提供get,或者只提供set,把属性变为只读或只写;
2、get方法可以提供权限的验证;Set方法可以提供数据有效性校验
3、可以隐藏内部属性的存放形式。
package com.lovo.family;
import java.util.Scanner;
public class MySelf {
public int a;
private int birthYear;
private int birthMonth;
private int birthDate;
private int age;
// 总结:get/set方法的封装体现
// 1、可以只提供get,或者只提供set,把属性变为只读或只写;
// 2、get方法可以提供权限的验证;Set方法可以提供数据有效性校验
// 3、可以隐藏内部属性的存放形式。
public int getAge() {
Scanner scan = new Scanner(System.in);
System.out.println("请输入权限:");
String password = scan.next();
if(password.equals("*****")){
return age;
}else{
return 0;
}
}
public void setAge(int age) {
if(age >= 18 && age <= 80){
this.age = age;
}
}
public String getBirthday(){
return this.birthYear + "-" + this.birthMonth + "-" + this.birthDate;
}
public void setBirthday(String birthday){
//把字符串birthday拆分为三个部分,再转换为整型存入三个属性中
}
public void test(){
this.a = 100;
}
}
静态成员
package com.lovo.bean;
//static的共性:
//1、凡是static的,都跟对象无关,都是类级别的
//2、凡是static的,都在加载期会有特殊处理
public class Student {
//当属性没有使用static修饰的时候:
//1、语法上,该属性必须用对象进行访问;
//2、内存上,该属性是放在每个对象身上的(每个对象有一个name属性);
//3、语义上,该属性是属于某个对象的;
//4、变量产生的时机,是在new对象的时候
//5、根据语义分析,很少有变量属性是static的
//6、普通属性、非静态属性
// public String name;
//当属性使用static修饰的时候:
//1、语法上,虽然可以用对象进行访问,但更多的是用类名直接访问;
//2、内存上,该属性是没有放在对象上的,而是存在数据段的静态区且全类共享一个值;
//3、语义上,该属性是跟类相关而跟对象无关的;
//4、变量产生的时机,是在加载类的时候;
//5、常量属性可以不用考虑,直接设计为public static final
//6、静态属性、类属性
public static String name;
//构造绝对不能用static
public Student(){
}
//没有用static修饰的方法
//1、语法上,必须使用对象进行调用
//2、非静态方法既可以操作静态属性也可以操作非静态属性,既可以调用
// 其它的静态方法也可以调用其他的非静态方法;
//3、加载上非static的方法被后加载
//4、语义上,如果该方法与某个学生对象有关,就只能设计为非static的
//5、设计上,如果方法的实现部分需要操作跟对象有关的属性或其他方法,
// 那么该方法就应该设计为非static的。
// public void study(){
// System.out.println(this.name + "说:好好学习,天天向上!");
// }
//用static修饰的方法
//1、语法上,虽然可以用对象进行调用,但更多的是用类名直接调用;
//2、静态方法只能操作静态属性,调用其他静态方法,甚至不能写this/super
//3、在加载上,static方法优先被加载;
//4、语义上,如果某个方法与学生对象无关,只与学生类有关,才设计为static的
//5、在设计上,如果该方法是工具类的工具方法,那么该方法应该设计为static的
public static void study(){
System.out.println(name + "说:好好学习,天天向上!");
}
}
静态初始化块和实例初始化块
package com.lovo.init;
public class PersonBean {
//实例初始化块
//该代码块在new对象的构造的时候会被自动执行,new一次就执行一次
//new对象构造方法要执行4步,该代码块在第3步之前执行,即在对属性进行初始化之前
//因此先在初始化块中对属性进行赋值,会被第3步的赋值覆盖
{
System.out.println("Person的实例初始化块");
}
//静态初始化块
//该代码块是在加载PersonBean的class的时候就被自动执行了
//一个类只加载一次,所以静态初始化块也只执行一次
//所以很多打开资源、开启链接的预加载动作可以写到静态初始化块中
static{
System.out.println("Person的静态初始化块");
}
//属性
private String name;
private int age = 20;
private static int a;
//构造
public PersonBean(){
System.out.println("PersonBean的构造");
}
public PersonBean(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;
}
}
package com.lovo.init;
public class MaleBean extends PersonBean {
{
System.out.println("MaleBean的实例初始化块");
}
static{
System.out.println("MaleBean的静态初始化块");
}
public MaleBean(){
System.out.println("MaleBean的构造");
}
}
内部类
外部类:
package com.lovo.innerclass;
//OutClass被称之为外部类
//所有的内部类都是一个独立的类,都拥有各自的属性、行为、构造等等
//每个内部类都会被编译成一篇独立的Class文件,文件名都带外部类类名$
public class OutClass {
private int a;
private static int b;
//成员内部类
//位置:直接写在外部类当中,位置和属性、构造、方法平行。
//Class文件的名字:”外部类类名" + $ + "内部类类名”
//可以有访问修饰符,代表是否可以在外部的类使用
//不管带什么访问修饰符,本类(外部类)的所有方法都可以使用
public class InnerClass1{
public void innerMethod(){
//内部类如果要调用外部类的属性,请书写下面格式:
OutClass.this.a = 100;
}
}
//静态内部类--成员内部类的特例
//位置:直接写在外部类当中,位置和属性、构造、方法平行。
//Class文件的名字:”外部类类名" + $ + "内部类类名”
//可以有访问修饰符,代表是否可以在外部的类使用
//不管带什么访问修饰符,本类(外部类)的所有方法都可以使用
public static class InnerClass2{
public void innerMethod(){
//静态内部类只能使用外部类的静态属性或静态方法,请书写下面格式:
OutClass.b = 100;
}
}
public void test1(final int e){
this.a = e;
final int A = 1200;
//局部内部类
//位置:直接写在外部类的方法当中,其位置和局部变量平行。
//Class文件的名字:”外部类类名" + $ + "序号" + "内部类类名”
//没有访问修饰符,只能在所定义的外部类方法中使用
class InnerClass3{
public void innerMethod(){
//局部内部类不能操作它所在方法的局部变量(非常特殊)
System.out.println(OutClass.this.a);
System.out.println(e);
}
}
//直接产生内部类对象
InnerClass3 in = new InnerClass3();
in.innerMethod();
}
public void test2(){
//匿名内部类--是局部内部类的特例
//位置:直接写在外部类的方法当中,其位置和局部变量平行。
//Class文件的名字:"外部类类名" + $ + "序号"
//在产生对象的同时去定义这个类,只能用这一次
new Object(){
public void introduce(){
//匿名内部类不能操作它所在方法的局部变量(非常特殊)
System.out.println("我可不是Object哦~~~");
}
}.introduce();
}
}
内部类:
package com.lovo.innerclass;
public class TestInnerClass {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//在外部使用成员内部类--首先产生外部类对象、然后使用特殊语法外部类对象.new
//产生内部类对象,然后再调用内部类对象的属性或方法(受访问修饰符限制)
OutClass out = new OutClass();
OutClass.InnerClass1 oi = out.new InnerClass1();
oi.innerMethod();
//在外部类使用静态内部类---可以用外部类类名直接访问操作,无需产生外部类对象
OutClass.InnerClass2 oi2 = new OutClass.InnerClass2();
}
}
Class文件的名字如下图:
多态
在OOP中,多态是指一个对象有多种形式的能力。一个类的子类可以定义他们唯一的行为,同事共享父类的某些相同特征。
多态就是:相同的行为,不同的实现。
多态可分为静态多态和动态多态。
- 静态多态指的是程序在编译时,系统就能决定调用哪个方法,也称编译时多态。
- 动态多态指在运行中系统才能动态确定方法所指对象,也称运行时多态。
多态的技术基础
- 向上转型技术
- instanceof关键字
- 动态绑定技术
引用数据类型转换,向上转型技术
package com.lovo.test;
import java.util.Scanner;
public class TestMain {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//1、基本数据类型的转型
//1-1、自动类型转换、隐式转换、从小范围的数据类型往大范围的数据类型转换
// 无风险\肯定成功
int a = 'b';
double f = 254;
//1-2、强制类型转换、显式转换、从大范围的数据往小范围的数据类型转换
// 有风险\丢失精度\无意义
a = (int)3.14;
char c = (char)12345670;
//2、引用数据类型的转换---只有有继承关系的类才能使用类型转换
//2-1、向上类型转换、自动类型转换、隐式类型转换、从小范围的数据类型往大范围的数据类型转换
// 无风险\肯定成功
PetBean p = new DogBean();//父类的引用可以指向子类的对象
//2-2、向下类型转换、强制类型转换、显式转换、从大范围的数据往小范围的数据类型转换
// 有风险/可能会抛出异常,终止程序的运行
//只有运行起来后,确实是本类引用指向本类对象或父类引用指向子类对象才可以成功
DogBean dog = (DogBean)p;
}
}
package com.lovo.bean;
public class PetBean {
private String name;
private int 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;
}
}
package com.lovo.bean;
public class DogBean extends PetBean {
private int boneNum;
public int getBoneNum() {
return boneNum;
}
public void setBoneNum(int boneNum) {
this.boneNum = boneNum;
}
}
instanceof关键字,判断运行时对象的真正类型。
- 判断一个对象是否是某个类的实例
- 返回只能是true/false
- 是一个关键字
语法:对象名 instanceof 类的实例(类名);
动态绑定技术:运行时根据父类引用变量所指对象的实际类型执行相应的子类方法,从而实现多态。
例:假如有个A类,A类中有个a()方法,有个B类继承A类,有个C类继承A类,B类中有个a()方法,C类中有个a()方法,编译完毕不能决定调用哪个方法,只有在调用a()方法运行时才能确定该方法是属于哪个对象的。
多态的主要应用
- 多态参数:当方法的某个形式参数是一个引用的时候,与该引用兼容的任何对象都可以传递给方法,从而允许方法接受不同数据类型的形式参数。父类的引用指向子类的对象,本类的引用指向本类的对象。
- 异构集合:多态最常见的的应用是创建一个不是同一类型但有共同父类的数据集合。不同对象的集合叫异构集合。如:
Object[] array = new Object[5];
array[0] = "hello";
array[1] = new Boy();
array[2] = new Scanner(System.in);
array[3] = new Date();
array[4] = 'A';//存进去的不是基本数据类型,而是基本数据类型对应的包装类对象
多态:
- 父类引用指向子类对象
- 该引用只能调用父类共有的方法,不能调用子类特有的方法
- 如果子类重写了父类的一个方法,那么调用的是子类重写后的方法
- 子类可以调用父类的所有方法
抽象
package com.lovo.bean;
//abstract关键字修饰类的时候,表示这个类是抽象类
//抽象类不能产生对象,它只能充当父类!
//1、有抽象方法的类一定是抽象类;
//2、抽象类不一定有抽象方法
//3、抽象类除了类有abstract修饰符,它内部跟普通类一样,可以有:
// 属性、构造、已经实现的方法。
public abstract class Girl {
//abstract关键字表示抽象
//当它修饰方法的时候,表示类有这个方法,但是不能确定这个方法的实现,应该由它的子类去确定
public abstract void response();
}
使用抽象类和抽象方法的好处:
- 抽象类不能产生对象,它只能充当父类
- OOP设计方法(典型而广泛使用):强制一个类的行为即规范子类的方法