一、类与对象
在计算机的世界中,面向对象程序设计的思想要以对象来思考问题,首先将现实世界的实体抽象为对象,然后考虑对象具备的属性和行为。
抽象出来的模型即为类,被抽象的事物即为对象(类实质上就是封装对象属性和行为的载体)。
例如:将动物的一些属性和行为抽象出来,封装成为类
public class Animal { //一个动物类
String name;
void sport() {
System.out.println("动物会行动");
}
void eat() {
System.out.println("吃食物");
}
}
public class Main {
public static void main(String[] args) {
//创建一个对象
Animal animal =new Animal(); //自动调用无参构造器
//调用它运动的方法
animal.sport();
}
}
this关键字与类的构造器
public class Animal { //一个动物类
String name;
public Animal(String name){
this.name=name; //使用this来区分类的数据于对象的属性
}
void sport() {
System.out.println("动物会行动");
}
void eat() {
System.out.println("吃食物");
}
}
public class Main {
public static void main(String[] args) {
//创建一个猫对象,调用构造器
Animal cat =new Animal("猫");
//调用它运动的方法
cat.sport();
}
}
二、面向对象的三大特性
一、封装
封装是面向对象的核心思想。将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。
同一个类中 | 同一包中 | 子类 | 所有类 | |
private | * | |||
default | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
public class Animal { //一个动物类
private String name;
public Animal(String name){
this.name=name; //使用this来区分类的数据于对象的属性
}
void sport() {
System.out.println("动物会行动");
}
void eat() {
System.out.println("吃食物");
}
public String GetName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
//创建一个猫对象,调用构造器
Animal cat =new Animal("猫");
//调用它运动的方法
cat.sport();
//cat.name 没有权限,无法访问
cat.GetName(); //公共方法,可直接访问
}
}
二、继承
public class Dog extends Animal { //继承了Animal
String name; //覆盖父类的name
public Dog(String name) { //子类构造器
this.name=name;
}
void talent() {
System.out.println("嗅觉灵敏");
}
}
public class Main {
public static void main(String[] args) {
Dog dog=new Dog("旺财");
dog.eat();//调用父类的方法
dog.talent();//调用自己的方法
}
}
运行结果:吃食物
嗅觉灵敏
继承中常用的一些方法
1.instanceof关键字 用来判断对象是否为类的对象public class Main {
public static void main(String[] args) {
Dog dog=new Dog("旺财");
System.out.println(dog instanceof Dog);
}
}
2.equals() 判断对象的引用是否相同
public boolean equals(Object obj) { //Object类中equals原码
return (this == obj);
}
3.toString() 返回对象引用的哈希值
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
继承的追溯
每个类都有继承(没有人为继承的类会自动继承Object类),并且每个类都有构造器,构造器的第一条语句总是:super()来调用父类对应的构造器,所以流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造器,知道当前子类为止。三、多态
多态的特点
方法重写
void eat() { //Animal类中的eat()方法
System.out.println("吃食物");
}
@Override
void eat() { //子类中重写的eat()方法
System.out.println("旺财喜欢吃骨头");
}
多态:
public class Animal { //一个动物类
private String name;
public Animal() {
}
public Animal(String name){
this.name=name; //使用this来区分类的数据于对象的属性
}
void sport() {
System.out.println("动物会行动");
}
void eat() {
System.out.println("吃食物");
}
public String GetName() {
return name;
}
}
ublic class Dog extends Animal { //继承了Animal
@Override
void eat() { //子类中重写的eat()方法
System.out.println("狗喜欢吃骨头");
}
}
public class Cat extends Animal { //继承了Animal类
@Override
void eat() { //重写子类的方法
System.out.println("猫喜欢吃鱼");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog(); //父类引用指向子类对象
Animal cat = new Cat(); //父类引用指向子类对象
//同为父类引用却产生不同的结果,即为多态
dog.eat();
cat.eat();
}
}
运行结果:狗喜欢吃骨头
猫喜欢吃鱼
重点:对象的转型
public class TestClassCast {
public static void main(String[] args) {
Animal a = new Animal("name");
Cat c = new Cat();
/**
* a instanceof Animal这句话的意思是a是一只动物吗? a是Animal这个类里面的是一个实例对象,所以a当然是一只动物,其结果为true。
*/
System.out.println("a instanceof Animal的结果是" + (a instanceof Animal));// true
/**
* 这里判断说“动物是一只猫”,不符合逻辑,所以打印出来的结果是false。
*/
System.out.println("a instanceof Cat的结果是" + (a instanceof Cat));
/**
* 父类引用指向子类对象
*/
a = new Dog();
/**
* 这里使用父类的引用对象a去访问子类对象里面新增加的成员变量是不允许的,
* 因为在编译器眼里,你a就是Animal类对象的一个引用对象,你只能去访问Animal类对象里面所具有的属性,
* 除了Animal类里面的属性可以访问以外,其它类里面的成员变量a都没办法访问。
* 尽管a指向的是子类Dog的一个实例对象,但因为子类Dog从父类Animal继承下来,
* 所以new出一个子类对象的时候,这个子类对象里面会包含有一个父类对象,
* 因此这个a指向的正是这个子类对象里面的父类对象,因此尽管a是指向Dog类对象的一个引用,
* 但是在编译器眼里你a就是只是一个Animal类的引用对象,你a就是只能访问Animal类里面所具有的成员变量, 别的你都访问不了。
* 因此一个父类(基类)对象的引用是不可以访问其子类对象新增加的成员(属性和方法)的。
*/
System.out.println("a指向了Dog,a instanceof Animal的结果是" + (a instanceof Animal));// true
/**
* 这里判断说“a是一只Dog”是true。 因为instanceof探索的是实际当中你整个对象到底是谁的实例 并不是根据你的引用把对象看出什么样来判断的。
*/
System.out.println("a instanceof Dog的结果是%s" + (a instanceof Dog));// true
/**
* 这里使用强制转换,把指向Animal类的引用对象a转型成指向Dog类对象的引用, 这样转型后的引用对象d1就可以直接访问Dog类对象里面的新增的成员了。
*/
Dog d1 = (Dog) a;
}
}
三、两个特殊类
一、抽象类
普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部分。
抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰。拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。
abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
抽象方法必须要重写。
二、内部类
定义在另一个类内部的类
内部类主要特点:
内部类可以很好的实现隐藏,可以使用public(默认)、protect、private修饰符。以前声明类的时候都是public,或者不写。外部类不能使用protect、private修饰符。内部类可以访问外部类所有的成员,包括私有的成员。
外部类不能访问内部类的成员和方法。必须建立内部类的对象才能访问。
内部类可以解决一些问题,比如间接的去实现多继承。
内部类可以避免修改接口,而实现同一个类中两种同名方法的调用。
内部类分类:
成员内部类1、成员内部类属于外部类的实例成员,成员内部类可以有public、private、default、protected权限修饰符。在成员内部类中访问外部类的成员方法和属性,要使用“外部类名.this.成员方法”和“外部类名.this.成员属性”的形式。
2、创建成员内部类的实例使用“外部类名.内部类名 实例名=外部类实例名.new 内部类构造方法(参数)”的形式。
3、成员内部类不能与外部类重名。
4、不能再成员内部类中定义static属性、方法、类(static final形式的常量定义除外)。因为一个成员内部类的实例必然与一个外部类的实例相关联。static成员完全可以移到其外部类中去。
class Outer {
private String name = "zhangsan";
private int num1 = 10;
public void outerShow() {
System.out.println(name);
System.out.println(num1);
//外部类不能直接访问内部类的成员和方法
// System.out.println(num2);
//外部类要想访问内部类的属性和方法,必须先产生内部类对象
Inner inner = new Inner();
inner.innerShow();
}
//private class Inner 私有的时候其他类不能创建内部类对象
public class Inner {// 是否对外公开
private String name = "lisi";
private int num2 = 20;
//静态的常量是可以的
private static final int num3 = 20;
// 在成员内部类中不能声明静态的成员,包括属性和方法
// private static int num3=30;
public void innerShow() {
System.out.println("----------");
System.out.println(name);
System.out.println(num2);
// 成员内部类可以直接访问外部类的属性和方法,包括私有的
System.out.println(num1);
outerShow();
//Outer.this外部类对象的引用,没有同名方法可以不加
System.out.println(Outer.this.name);
Outer.this.outerShow();
}
}
}
静态内部类
2、静态内部类和外部类没有任何关系,只是在生成类名和类定义的时候有影响。静态内部类可以看做是与外部类平级的类。使用方式与外部类平级的类完全相同。
3、创建静态内部类的实例:外部类名.内部类名 实例名 = new 外部类名.内部类名(参数);
4、静态内部类不能与外部类重名
5、静态内部类不能访问外部类的非静态的属性和方法。外部类不能访问内部类的非静态的属性和方法。
(把静态内部类当做外部类的静态方法来看)
class Outer {
private String name = "zhangsan";
private int num1 = 10;
private static int num3 = 100;
public void outerShow() {
System.out.println(name);
System.out.println(num1);
// 不能访问
// System.out.println(Inner.name);
// 可以通过内部类对象进行访问
Inner inner = new Inner();
System.out.println(inner.name);
// 可以访问内部类中的静态属性
System.out.println(Inner.num3);
}
public static class Inner {
private String name = "lisi";
private int num2 = 20;
private static int num3 = 30;
public void innerShow() {
// 静态内部类不能访问外部类中非静态成员
// System.out.println(Outer.this.name);
// 可以访问自己内部的
System.out.println(num2);
// 通过类名访问
System.out.println(Outer.num3);
}
}
}
匿名内部类
1、匿名内部类是没有名称的内部类,没有办法引用它们。必须在创建的时候作为new语句的一部分来声明并创建它们的实例。
2、匿名内部类必须继承一个类(抽象的、非抽象的都可以)或者实现一个接口。如果父类(或者父接口)是抽象类,则匿名内部类必须实现其所有的抽象方法。
3、匿名内部类中可以定义代码块{},用于实例的初始化(代码块优先构造方法先执行),但是不能定义静态代码块。
4、new interface/superclass{ }
这种形式的new语句声明一个新的匿名类,它对一个给定的类进行扩展,或者实现一个给定的接口,并同时创建该匿名类的一个新实例。new 出来的是接口/抽象类的实现类对象。
new Animal() {
// 匿名内部类中可以有自己的属性
private String name = "dog";
// static不能有静态代码块
{
name = "haha";
}
@Override
public void eat() {
System.out.println("吃鱼");
}
// 匿名内部类中可以有自己的方法
public void show() {
System.out.println("show " + name);
}
}.show();// 调用自己的方法,只能通过这个方法
}
四、接口
public interface MyInterface {
// 接口中只有常量,抽象方法
String MAX_JOB = "BOSS";
int MAX_SPEED = 120;
//在接口中定义的均为常量,会自动添加public static final
public void test();
public void test(int a);
//接口中定义的方法会自动添加abstract
//实现了设计和实现的分离
}
接口就是比“抽象类”更抽象的“抽象类”,可以更加规范的对子类进行约束,全面的专业的实现了:规范和具体实现的分离。
接口就是规范,定义的是一组规则,体现了现实世界中,”如果你是,那么你就能“的思想。
接口支持多实现。
接口的实现必须要重写。
五、static与final关键字
一、static关键字
static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。 被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。 只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
1、static变量 按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。 两者的区别是: 对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。 对于实例变量,每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
2、静态方法 静态方法可以直接通过类名调用,任何的实例也都可以调用,因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。 因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。 例如为了方便方法的调用,Java API中的Math类中所有的方法都是静态的,而一般类内部的static方法也是方便其它类对该方法的调用。 静态方法是类内部的一类特殊方法,只有在需要时才将对应的方法声明成静态的,一个类内部的方法一般都是非静态的
3、static代码块
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。
二、final关键字
final常量
当你在类中定义变量时,在其前面加上final关键字,那便是说,这个变量一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对于对象变量来说其引用不可再变。其初始化可以在两个地方,一是其定义处,也就是说final在变量定义时直接给其赋值,二是在构造函数中。这两个地方只能选其一,要么在定义时给值,要么在构造函数中给值,不能同时既在定义时给了值,又在构造函数中给另外的值。
final方法
将方法声明为fianl,那就说明你已经知道这个方法提供的功能已经满足你要求,不需要进行扩展,并且也不允许任何从此类继承的类来覆写这个方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。final类
当将final用于类身上时,一个final类是无法被任何人继承的,那也就意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。对于final类中的成员,你可以定义其为fianl,也可以不是final。而对于方法,由于所属类为final的关系,自然也就成了final型的。
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!