面向对象
前言
OO(Object-Oriented:面向对象)
OOA(Object-Oriented Analysis:面向对象分析——高级——架构师)
OOD(Object-oriented Design:面向对象设计——中级)
OOP(Object Oriented Programming:面向对象的程序开发——初级)
面向过程编程:以过程为中心的编程思想,分析出解决问题的步骤,然后用函数把这些步骤一步一步实现。数据和数据操作是分离的。
结构化编程:在程序设计的早期,程序用流程图和自顶向下的方法设计。采用这种设计方法,程序员会将一个大的问题分解成更小的任务,然后为每个更小的任务编写一个过程(或函数、方法)。最后,程序员会编写一个主过程来启动程序流程,随后根据程序流程走向,调用想要的其它过程。这种类型的程序设计被称为结构化编程。
面向对象编程:将事物对象化,通过对象通信来解决问题。数据和数据操作绑定在一起的。
src——sourse
主方法——main,不是类的组成部分,只是程序的入口
面向过程——结构化——面向对象(OO)
基本的场景——注册、登录
public static void zhuce() {
// 代码块
}
1 对象和类
分类class
根据共有的特性将代码分类
类是模板,确定对象将会拥有的属性和方法。特点:具有相同属性和方法的一组对象的集合。
1.1 变量、对象
1.1.1 创建
创建对象:声明(对象名称和对象类型);实例化(new创建);初始化(使用new创建对象时,会调用构造方法初始化对象)
定义这个类的属性的位置范围
public class Test01() {
int a = 111; // 类的属性,a是这个类的一个组成部分,属性就是类的成员变量
public static void main(String[] args) {
int b = 222; // 不属于类的属性,b这个变量是main方法的组成部分,局部变量
}
}
public class Test() {
public static void main(String[] args) {
Test01 test01 = new Test01();
// test01是Test01类型的一个变量
// test01赋值:1.创建值new 2.新建的对象 3.赋值给test01变量
System.out.println(test01.a); // 111
// 取不到b昂,b是局部变量哈
}
}
直接输出值的就是变量;输出内存地址的就是对象(类的一个实例,有状态和行为);类是一个模板,描述一类对象的行为和状态。
类是对象的抽象,对象是类的实例。
1.1.2 局部变量
局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明(必须声明
)和初始化都是在方法中,方法结束后,变量就会自动销毁。
生命周期:调用对应方法时,执行完创建语句而存在,超出自己的作用域立即从内存消失;
初始化:没有默认初始值,需要初始化后才能使用。
1.1.3 成员变量
成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
生命周期:随对象的创建而创建,随对象的消失而消失;
初始化:有默认初始值。
1.1.4 类变量
类变量:类变量也声明在类中,方法体之外,但必须声明为static类型(静态变量)。
1.1.5 局部变量和成员变量的区别:
1)默认值:成员变量有默认值初始化;局部变量没有默认初始化,必须定义,赋值,才能使用。
2)作用域:成员变量在类中方法外定义,存放于堆内存;
局部变量在方法定义中或者方法声明,作用于整个方法,存放于栈内存;
3)修饰符:成员变量只能用final
4)生命周期:成员变量随着对象的创建而存在,随着对象的消失而消失。局部变量随着方法的调用而存在,随着方法的调用完毕而消失。
注意:
局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则。
1.2 常量
关键字final指示常量。
final double PI = 3.14;
1.3 内存地址比较
new 初始化 新的地址
public class Pro_04 {
public static void main(String[] args) {
Pro_01 pro_01 = new Pro_01(); // 这是同一个包里的一个类
System.out.println("Pro_01:" + pro_01); // Pro_01:java201.Pro_01@15db9742
// 数组类型
int[] nums = {1,2,3,4};
System.out.println(nums); // [I@15db9742
String str01 = "123";
String str02 = "123";
String string01 = new String("123");
String string02 = new String("123");
String string03 = string01;
System.out.println(str01); // 123
System.out.println(str02); // 123
System.out.println(str01 == str02); // true
System.out.println(string01 == string02); // false
System.out.println(string01.equals(string02)); // true
System.out.println(string01 == string03); // true
}
}
1.4 GC
点击链接 查看GC,JVM——https://blog.youkuaiyun.com/weixin45044097/article/details/96108242
1.5 finalize()方法
Java 允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象。
例如,可以使用 finalize() 来确保一个对象打开的文件被关闭了。
在 finalize() 方法里,你必须指定在对象销毁时候要执行的操作。
finalize() 一般格式是:
protected void finalize() {
// 在这里终结代码
}
关键字 protected 是一个限定符,它确保 finalize() 方法不会被该类以外的代码调用。
当然,Java 的内存回收可以由 JVM 来自动完成。如果你手动使用,则可以使用上面的方法。
1.6 类的构建过程
执行过程:静态(最优先)——父类——子类{属性 -> 构造器}
class ClassA {
public ClassA() {
System.out.println("ClassA()");
}
}
class ClassB {
public ClassB() {
System.out.println("ClassB()");
}
}
class ClassC {
ClassA a = new ClassA();
ClassB b;
public ClassC() {
System.out.println("ClassC()");
b = new ClassB();
}
}
public class TestConstructer {
public static void main(String args[]) {
ClassC cc = new ClassC();
// 输出 ClassA() ClassC() ClassB()
}
}
2 包、修饰符
2.1 包(package)
操作系统、程序中如何定义
分包原因:
- 文件数量越来越多
- 功能性要求
- 解决重名
引入(import)所在包没有找到需要的类型,就通过名字引入一个已经存在的类(项目中);
在所在包新建一个对应的类型;
不同包——必须引入;
import java.lang.*;
开发中引入非lang包的时候,一定不要用星号,*通配符,代表所有,用一个引入一个。
2.2 修饰符
类、方法、成员变量 都有一个访问级别的修饰符。
开放的程度由 大——>小:
权限修饰符 | 使用范围 |
---|---|
public | 本类,同包,子类 (同一项目中) |
protected | 本类,同包,子类 (通过子类对象在子类范围内部可以访问) |
default | (package)本类,同包 |
private | 本类 |
注意protected:
- 基类的 protected 成员是包内可见的,并且对子类可见;
- 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。
3 方法
语句的集合,在一起执行一个功能。
- 解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
优点:
- 程序更加简短清晰
- 有利于程序维护
- 提高程序开发效率
- 提高代码重用性
3.1 方法组成
组成:修饰符, 返回值类型, 方法名, 形式参数, 方法体
3.1.1 修饰符
定义该方法访问类型。
3.1.2 返回值类型
void,int,double,String,
3.1.3 方法名
方法实际名字,规则:
方法的名字第一个单词以小写字母开始,后面的单词首字母大写,不使用连接符。
3.1.4 形参
参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
3.1.4.1 形参和实参的理解
1.形式参数出现在方法定义中,在整个方法体内可以使用,离开方法不能使用。只有在被调用时才分配内存单元,调用结束即刻释放所分配的内存单元;
2.实际参数出现在主调函数中,进入被调方法后,实参变量也不能使用(实参可以是常量,变量,表达式,函数等)
3.形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。
4.实参和形参在数量上,类型上,顺序上应严格一致,否则会发生“类型不匹配”的错误
5.当形参和实参不是指针类型时,在该函数运行时,形参和实参是不同的变量,他们在内存中位于不同的位置,形参将实参的内容复制一份,在该函数运行结束的时候形参被释放,而实参内容不会改变。
如果函数的参数是指针类型变量,在调用该函数的过程中,传给函数的是实参的地址,在函数体内部使用的也是实参的地址,即使用的就是实参本身。所以在函数体内部可以改变实参的值。
1)形参为基本类型时,对形参的处理不会影响实参。
2)形参为引用类型时,对形参的处理会影响实参。
3)String,Integer,Double等immutable类型的特殊处理,可以理解为值传递,形参操作不会影响实参对象。
3.1.4.2 基本类型和引用类型
Java中的变量分为基本类型和引用类型两种。
基本类型的变量保存数值本身,而引用类型的变量保存的是引用值,即指向内存空间的地址。
基本类型(四类八种)包括:
byte,char,int short,long,float,double,boolean;
引用类型包括:类类型,接口类型和数组。
区别:
- 基本类型在声明变量是就为它分配了空间;int a; a=10;
- 在进行引用声明时只给变量声明了引用空间,而不分配数据空间。
(引用也是占用空间,一个空对象的引用大小大概是4Byte)
3.1.4.3 引用传递和值传递
- 值传递:方法调用时,实际参数将它的值传递给对应的形式参数,函数接收到的是原始值的副本,此时内存中存在两个相等的基本类型,若方法中对形参执行处理操作,并不会影响实际参数的值。
- 引用传递:方法调用时,实际参数的引用(是指地址,而不是参数的值)被传递给方法中相应的形式参数,函数接收到的是原始值的内存地址,在方法中,形参与实参的内容相同,方法中对形参的处理会影响实参的值。
关于Java对象作为参数传递是传值还是传引用的问题:
(参考一下别人的博客,个人觉得写的不错昂~)
Alan_Xiang的博客 https://blog.youkuaiyun.com/xiangwanpeng/article/details/52454479
3.1.5 方法体
方法体包含具体的语句,定义该方法的功能。
3.2 方法调用
调用两种方式:
- 对象名.方法名([参数列表])
- this调用
返回值两种方式: - 当方法返回一个值的时候,方法调用通常被当做一个值。
- 如果方法返回值是void,方法调用一定是一条语句。
3.3 方法重载和重写
3.3.1 方法重载
重载overload:同一个类中,方法名一样,参数列表不同(参数类型、个数、顺序)。
重载跟修饰符,返回值类型没有关系。
规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型,无法以返回值类型作为重载函数的区分标准;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
3.3.2 方法重写
重写override:父类的方法不满足子类的需求时,子类对父类的允许访问的方法的实现过程进行重新编写(定义), 返回值和形参都不能改变。即外壳不变,核心重写!
好处:
子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
规则:
- 参数列表必须完全与被重写方法的相同;
- 访问权限不能比父类中被重写的方法的访问权限更低。(父类权限<=子类权限)例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
- 父类的成员方法只能被它的子类重写。
- 声明为final的方法不能被重写。
- 声明为static的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。 ( 例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。)
- 构造方法不能被重写。
- 如果不能继承一个方法,则不能重写这个方法。
3.3.3 重载和重写区别
区别点 | 重载方法 | 重写方法 |
---|---|---|
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可以修改 | 一定不能修改 |
异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 |
访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
3.4 构造方法
3.4.1 构造方法定义声明
名字(修饰符)必须和所在类的名字一致,没有返回值,不能声明void,访问权限任意,一般情况下是public。参数不同的构造方法构成重载。
一旦你定义了自己的构造方法,默认构造方法就会失效。不管是否自定义构造方法,所有类都有构造方法。
3.4.2 构造方法的使用
- 跟在关键字new后面,类名加小括号();
Study stu(引用)
= new Study();(对象)
- 跟在关键字super或者this后加小括号();
public Demo() {
this(2); // 调用参数为int类型的本类构造方法
}
public Demo() {
super(1); // 调用参数为int类型的父类构造方法
}
注意:this或super调用构造方法只能出现在构造方法中,而且必须出现在第一行,所以一个构造方法中第一行只能为this或super调用构造方法,两者不能同时调用构造方法出现,而且注意this或super调用构造方法时,要留构造方法出口,意思就是最后调用的构造方法中没有再调用别的构造方法!
栈:引用 变量
堆:所有对象
池:常量(不能重复)
public class Study008 {
int a = 111;
public int setA(int a) {
a = 222;
return a;
}
public Study008 setA(Study008 s) {
s.a = 444;
return s;
}
// static静态修饰符 静态的只能使用静态的
public static void main(String[] args) {
Study008 s = new Study008();
int a = 555;
// 传入参数调用方法时,传入的是变量或对象的值,而不是变量或对象本身
s.setA(a);
System.out.println("s.setA(a):" + s.setA(a)); // 222
//值传递,调用方法时,传入一个基本类型的变量,它传递的不是变量本身而是变量的值
System.out.println("a:" + a); // 555
System.out.println("s.a:" + s.a); // 111
s.setA(s);
System.out.println("s.setA(s):" + s.setA(s)); // student201.Study008@15db9742
System.out.println("a:" + a); // 555
// 实参是类类型的对象:1.方法中参数被赋值为null.外部对象不会改变
// 2.方法中参数的属性赋值被改变,外部对象属性也会改变
System.out.println("s.a:" + s.a); // 444
}
}
3.4.3 构造方法的作用
通常会使用构造方法给一个类的实例变量赋初值,或者执行其他必要的步骤来创建一个完整的对象。
- 为了初始化成员属性,而不是初始化对象,初始化对象是通过new关键字实现的;
- 通过new调用构造方法初始化对象,编译时根据参数签名来检查构造函数,称为静态联编和编译多态;
(参数签名:参数的类型,参数个数和参数顺序) - 创建子类对象会调用父类构造方法但不会创建父类对象,只是调用父类构造方法初始化父类成员属性。
3.4.4 构造器和普通方法区别
- 作用:构造器创建对象和初始化对象的值,成员方法使对象具有某种行为;
- 返回值:构造方法前没有返回值类型的声明,没有return语句;普通方法有返回类型;
- 方法名:构造方法名和类名相同,普通方法名小写不能与类名相同;
- 调用:创建对象时调用new,(Book book = new Book();)普通方法在构造器创建对象后使用“.”调用。
3.4.5 构造器之继承
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。
如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器( 默认隐式调用super() )。
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 类继承
class SubClass extends SuperClass{
private int n;
SubClass(){ // 自动调用父类的无参数构造器
System.out.println("SubClass");
}
public SubClass(int n){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
// SubClas2 类继承
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自动调用父类的无参数构造器
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
public class TestSuperSub{
public static void main (String args[]){
System.out.println("------SubClass 类继承------");
SubClass sc1 = new SubClass(); // SuperClass() SubClass
SubClass sc2 = new SubClass(100); // SuperClass(int n) SubClass(int n):100
System.out.println("------SubClass2 类继承------");
SubClass2 sc3 = new SubClass2(); // SuperClass(int n) SubClass2
SubClass2 sc4 = new SubClass2(200); // SuperClass() SubClass2(int n):200
}
}
(部分资料参考菜鸟教程——java教程)
4 继承
4.1 概念
当前类派生出新的类,当前类成为父类,派生出来的类成为子类,子类可以拥有父类属性和方法,子类也可以扩展自己的属性和方法,这个过程称为继承。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
4.2 继承特性
1.子类拥有父类非 private 的属性、方法,可以拥有自己的属性和方法,即子类可以对父类进行扩展。
2. 子类可以用自己的方式实现父类的方法。
3. Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
4. 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
5. 执行过程:静态(最优先)——父类——子类{属性 < 构造器}
6. 继承初始化顺序:
1) 初始化父类再初始化子类
2)属性的初始化在构造方法之前执行。
3) 父类的属性初始化–>父类的构造图数–>子类的属性初始化->子类的构造函数
7. 子类访问限制:同包下子类可以访问分类除开私有的(private)所有的属性和方法; 不同包下的子类可以访问父类的public和protected的属性和方法。
class A {
public A() {
System.out.println("A");
}
public A(int a) {
System.out.println("A(int)");
}
}
public class Person {
static {
System.out.println("person静态最优先");
}
A a = new A();
public String name;
public Person() {
System.out.println("person()");
a = new A(12);
}
void eat() {
System.out.println("-----eat");
}
void sleep() {
System.out.println("-----sleep");
}
}
class B{
public B() {
System.out.println("B");
}
public B(int a) {
System.out.println("B(int)");
}
}
public class Student extends Person{
static {
System.out.println("student静态最优先");
}
static B b = new B(12);
public Student() {
System.out.println("student----");
b = new B();
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println("------------");
student = new Student();
}
}
结果:
4.3 super与this关键字
4.3.1 super
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象(this)的父类对象。
- 属性:当父类属性和子类属性重复定义时;
- 方法:
- 构造器:super([参数列表])
4.3.2 this
this关键字:指向自己的引用。
1.调用属性:当成员变量和局部变量名重名,想要调用成员变量就要用this.属性名
。
2.调用方法:this.方法名称([参数列表])
3.调用构造器:当一个构造器想调用另一个构造器完成初始化过程,必须使用this( [参数列表]);
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat(); // animal:eat
Dog d = new Dog();
d.eatTest(); // dog:eat
// animal:eat
}
}
super和this异同:
- super()和this()均需放在构造方法内第一行。
- super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
- this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
- super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
- 调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
- super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。
- 尽管可以用this调用一个构造器,但却不能调用两个。
- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
4.4 final关键字
1.final修饰类:声明类可以把类定义为不能继承的,即最终类。(断子绝孙类,不能有子类)
2.final修饰变量:一旦被赋值,不能改变
局部变量:使用之前必须要赋值;
成员变量:必须有初始值(在定义的时候可以没有值,但在构造器必须进行初始化)
3.final修饰方法:该方法不能被子类重写,即最终方法。
注意:
被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final。
4.5 枚举
枚举是限定有限可能值的一种手段,使用枚举可以降低程序出错的几率,并可以提高代码的可读性与可维护性。Java中的枚举并不是简单常量的集合,而是一个对象,其本质依然是类,所以Java中的枚举除了提供一系列相关值以外,还提供了一些额外功能,甚至还可以根据需要自行添加一些功能。
4.5.1 概念
尽管Java中的枚举是对象,但是大多数使用它的场景与其它语言中并无二致,因此其使用形式也大抵相同。
定义:关键字enum。
例如一个应用需要使用一系列特定的颜色值,则可以定义一个类似以下形式的枚举:
enum MyColor {
RED,
BLUE,
GREEN;
}
4.5.2 使用
枚举定义完成以后,就可以以“枚举名.枚举项”的形式在代码中对定义的枚举进行使用。在特殊情况下,如果程序能够识别出当前的枚举类型,则只需要给出枚举项即可,例如在switch语句块中的case关键字后的常量值。
MyColor color = MyColor.RED;
switch(color){
case RED:
System.out.println("红");
break;
case BLUE:
System.out.println("蓝");
break;
case GREEN:
System.out.println("绿");
break;
default:
System.out.println("未知色");
break;
}
4.5.3 常用方法
1.对于枚举对象,主要可用的方法为values,它返回当前枚举中定义的所有枚举项的集合;
2.对于枚举项,主要可用的方法有ordinal、name和getDeclaringClass。方法ordinal返回枚举项在枚举对象中的序号;方法name则返回枚举项的名称(与方法toString效果相同),通常用于取得枚举变量中保存的枚举项名称;而方法getDeclaringClass则用于取得当前枚举值所在类的完整名称。
3.枚举项之间还可以通过方法compareTo进行比较,如果参数传入的枚举项与当前值相等,则返回0。
4.5.4 为枚举添加构造器和成员方法
类拥有构造器,枚举是一种特殊的类,所以枚举也可以拥有自己的构造器。但与普通类的不同之处在于枚举的构造器不可以是public的,其原因在于该构造器是提供给枚举对象中的枚举项构造时使用的,它并不需要在枚举对象之外使用。
可以为枚举添加构造器一样也可以为枚举添加方法。例如上述代码中通过构造器为每个枚举项添加了中文说明以及其对应的Color信息,那么为了取出枚举项对应的信息,则需要为枚举MyColor添加如下相应方法.
enum MyColor {
RED("红色",Color.RED),
BLUE("蓝色",Color.BLUE),
GREEN("绿色",Color.GREEN);
private String summy;
private Color color;
private MyColor(Stringsummy, Color color) {
this.summy = summy;
this.color = color;
}
}
public String getSummy() {
return this.summy;
}
public Color getColor() {
return this.color;
}
4.5.5 枚举集合
Java中的枚举是一个对象,枚举对象中的各枚举项也是对象。在Java中,集合容器要求存储在其中的每一个成员均是对象类型,而枚举作为一种特殊的类,Java亦专门为枚举提供了集合容器EnumSet和EnumMap。
还可以参考一下别人写的枚举详解:
https://blog.youkuaiyun.com/qq_27093465/article/details/52180865
4.6 补充
4.6.1 美国时间获取
// 获取日历月份
int month = Calendar.getInstance().get(Calendar.MONTH) + 1;
4.6.2 Object类
所有类,没有明确指定,所有类的父类都是Object
4.6.3 static关键字
4.6.3.1 用途
用途:修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
4.6.3.2 使用
1.static方法:
静态方法:不依赖于任何对象就可以进行访问
注意:在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量;不能用this,super,更没有对象
2.static变量:
静态变量:属于类,在内存中只有一个复制,只要静态变量所在的类被加载,这个静态变量就会被分配空间,因此就可以被使用了。
使用:1.类.静态变量 2.对象.静态变量
3.static代码块:
static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
4.6.3.3 注意
1.static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的;
2.不能修饰局部变量;
4.6.3.4 单例设计模式
该类只能有一个实例,为了实现这一功能,必须隐藏类的构造函数,即把构造函数声明为private,并提供一个创建对象的方法(getInstance),由于构造对象被声明为private,外界无法直接创建这个类型的对象,只能通过该类提供的方法来获取类的对象,要达到这样的目的只能把创建对象的方法声明为static
4.6.3.4 static和final结合
1.对于变量,若使用static final修饰,表示一旦赋值不能修改,并且通过类名可以访问
2.对于方法,若使用static final修饰,表示该方法不可被覆盖,并且可以通过类名直接访问;
4.6.4 重写toString()、equals()、hashCode()
toString:属性的值作为返回值
// toString()源码
public String toString() {
return this;
}
// 重写toString()方法
// equals源码
// 当前对象的属性和参数的对象是否一致
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
// hashCode()源码
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
5 多态
一种事物多种形态。
应用:
1.静态多态——方法的重载
2.动态多态——方法的重写和向上转型
5.1 向上、下转型
向上转型:一个父类类型的变量,指向一个子类类型对象。(把子类类型的对象指向父类类型的引用)
Animal animal = new Dog();
Animal animal引用:可以访问 父类没有被重写方法 和 子类重写的方法。
(java.ClassCastException类型转换错误)
向下转型:把一个子类类型的对象,由父类类型转换为原来的子类类型。 (前提是向上转型了的)
Dog dog = new Dog();
Animal animal = new Dog();
animal = new Animal();
if(annimal instanceof Dog) {
dog = (Dog)animal;
}
animal=dog;
if(animal instanceof Dog){
//可以向下转型
dog = (Dog)animal;
dog.fei();
}
class Super{
public void method(){
System.out.println("method() in Super");
}
public void method(int i){
System.out.println("method(int) in Super");
}
}
class Sub extends Super{
public void method(){ // 重写父类的方法
System.out.println("method() in Sub");
}
public void method(String str){
System.out.println("method(String) in Sub");
}
}
public class TestSuperSub{
public static void main(String args[]){
Super s = new Sub();// 子类对象给父类引用
s.method(10); // method(int) in Super 父类没有被重写的方法
s.method(); // method() in Sub 子类重写了的方法
s.method("hello"); // 错误
}
}
5.2 抽象类
5.2.1 概念
抽象类:不能使用new方法进行实例化的类,即没有具体实例对象的类,抽象类有点类似于“模板”的作用,目的是根据其格式来创建和修改新的类,对象不能由抽象类直接创建,只可以通过抽象类派生出新的子类,再由其子类来创建对象。
优点:具体类可从抽象类自动得到这些方法的缺省实现
注意:抽象类可以有构造函数;
抽象方法:当你不明确该方法行为,就可以把这个定义为抽象方法;
作用:起到模板和约定的作用;
注意:抽象方法没有方法体;抽象类不能被实例化;普通类去继承抽象类,必须实现抽象类所有抽象方法。
抽象类不一定有抽象方法,有抽象方法的类一定是抽象类;
6 接口
6.1 概念
接口里的方法都是抽象的,普通类实现接口,必须实现接口里全部的方法。
java类支持单继承,多实现;
实现接口关键字:implements
作用:
1.规范模板
2.实现了java多继承的特点(接口可继承多个接口)
// 接口与接口之间可以多继承
public interface 接口名 extends 接口1,接口2,接口n{
static final String PATH = ""; // 默认常量静态属性
// 抽象方法
public abstract void a(); // 规范模板的作用,默认修饰符public
}
InterfaceA ia = (InterfaceA )new ClassA();
// 编译通过,运行报java.ClassCastException类型转换错误
// 接口嘛,可以任意实现的
6.2 接口和抽象类的区别
同:接口和抽象类都不能实例化,都可以声明引用,都可以有属性
异:接口里面全是抽象方法,不能有非抽象方法,而抽象类里面可以有非抽象方法。
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
6.3 jdk1.8之后
jdk1.8之后,接口中可以有default方法,static方法,并且两个可以有方法体。
public interface MyInterface {
public abstract void eat();
default void sleep() {
System.out.println("默认方法");
}
static void breath() {
System.out.println("静态方法");
}
}