目录
对面向对象语言的理解
在谈论面向对象之前,我们需要先了解面向过程语言设计思想,过程就是具体步骤,把要完成的一件事情分成一步一步的步骤,然后逐步完成,比如说洗衣服,第一步打开洗衣机盖 (),第二步衣服扔进去(),第三步关闭洗衣机盖(),是注重做什么的步骤。而面向对象语言设计思想,首先会宏观的对整个事物进行分析也就是分类,不关心具体要做什么,而是关心谁去做,也就是哪个对象去做,我们就可以把洗衣机和人看做两个类,洗衣机有开盖()、关盖()、洗衣服等方法,人有操作()这个方法,面向过程和面向对象是相辅相成的,面向对象是宏观的设计,面向过程是具体的实现。
类和对象
类就是对同一类具有共同属性和行为事物的一种抽象
类是一个模板,我们根据某个类皆可以来创建这个类的具体实例,也就是对象
对象是类的实例 是概念的具体存在 以类为模板,在内存中创建一个真实存在的实例
万事万物(现实存在)都可以看做对象(可以使用的)
什么时候加载类
在类被主动使用时,类再被首次使用时
- new 对象
- 访问静态变量
- 访问静态方法
- 对某个类进行反射操作
- 子类初始化导致父类初始化
- 执行该类的mian()
类中元素加载顺序
父类静态—>子类静态—>父类普通属性—>父类代码块—>父类构造方法—>子类普通属性—>子类代码块—>子类构造方法
静态的按先后顺序
创建对象的几种方式
- new 对象
- 克隆 Object类里的clone()
- 反射 使用Class类创建实例
- 反序列化
- String s = “abc”
类中的变量
从数据类型角度分
基本类型变量: byte short int long float dobule char boolean
引用类型变量: 类 数组 接口…
按照位置划分
成员变量
成员变量直接被类包含,存在与类中方法外。非静态的成员变量在创建对象的时候会从类中复制一份到对象中,对象创建的时候产生,对象被回收的时候销毁。静态的成员变量一个类中只有一份,会被所有对象会共享,在类加载时创建,链接中准备环节会赋默认值,初始化时赋值,类卸载的时候消毁;但是除了静态常量,静态常量也就是被static final修饰的会在编译期间赋值。
局部变量
存在与在方法或者代码块中,局部变量在使用前必须显示的初始化赋值,方法被调用时出生,存处于栈帧中的局部变量表中,如果出现需要计算的局部变量,会先在操作数栈中计算完成后再存入局部变量表中,局部变量会在方法运行结束也就是出栈的时候被销毁。
类中的方法
方法是类或对象行为特征的抽象,用来完成某个功能操作,将功能封装为方法的目的是,可以实现代码重用,简化代码
成员方法
定义在类中,实例方法可以被对象调用,完成对象的功能
静态方法可以直接使用类名调用
形参和实参
形参主要用于将方法体外的数据内容带入到方法体内部
实参就是实际存在的参数,必须有确定的值
构造方法
构造方法就是在创建对象的同时明确对象的属性值,用来初始化创建出来的对象。
方法名与类名相同,不需要void修饰,没有返回值
每一个类中至少有一个默认无参的构造方法,如果显示的定义一个构造方法,默认的就失效了
class Person {
private int age;
private String name;
Person(int a, String nm) { // Person的构造方法,拥有参数列表
// 接受到创建对象时传递进来的值,将值赋给成员属性
age = a;
name = nm;
}
}
public class MainText {
public static void main(String[] args) {
Person person = new Person(11,"张三"); //在创建对象同时需要传参
}
}
方法重载和重写
重载
在一个类中可以创建多个方法,他们具有相同的名字,但有不同的参数列表,调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法
重载的时候,方法名要一样,但是参数类型和个数不一样,与方法返回值无关
重写
重写就是子类在继承父类时,可以对父类原有的方法进行重写,需要方法名、参数列表,返回类型都相同,子类方法的访问权限不能低于父类
静态方法,构造方法,属性都不存在重写
对象与引用
基本类型
基本类型变量存储的是一个确定类型的值,java有8大基本类型,在创建的时候,内存大小是已知的,系统会为你在虚拟机栈上分配相应的内存空间,存储相应变量的值
引用类型
引用类型就是“引用”了一个对象,引用类型的引用存和基本类型一样储在虚拟机栈上,使用完会自动释放,而指向的对象存储在虚拟机堆中,由垃圾回收器管理,引用类型变量只是拥有对象的地址,通过地址间接访问对象
引用传?递值传递
- 引用传递是指在调用方法时将实际参数的地址直接传递到方法中,那么在方法中对参数所进行的修改,将影响到实际参数。
- 值传递是指在调用方法时将实际参数拷贝一份传递到方法中,这样在方法中如果对参数进行修改,将不会影响到实际参数
Java是值传递。当传的是基本类型时,传的是值的拷贝,对拷贝变量的修改不影响原变量;当传的是引用类型时,传的是引用地址的拷贝,但是拷贝的地址和真实地址指向的都是同一个真实数据,因此可以修改原变量中的值;当传的是String类型时,虽然拷贝的也是引用地址,指向的是同一个数据,但是String的值不能被修改,因此无法修改原变量中的值
static
static 修饰符可以修饰成员变量、方法、代码块、内部类
它所修饰的成员
- 随着类的加载而加载
- 先于对象而存在
- 可以被所有的对象共享
- 可以不创建对象,直接使用类名调用
- 静态方法不能调用非静态成员
修饰成员方法
static修饰的方法一般称作静态方法,静态方法不依赖于任何对象就可以进行访问,对于静态方法来说是没有this
在静态方法中不能访问类的非静态成员变量和非静态成员方法
最常见的static方法就是main方法,因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问
修饰成员变量
static修饰的变量也称为静态变量,是被所有对象共享,在内存中只有一个副本,在类首次加载时会被初始化,静态常量除外
修饰代码块
static块可以优化程序性能,是因为它只会在类被初次加载的时候执行一次
将一些只需要进行一次的初始化操作都放在static代码块中进行(避免重复运行,提高效率)
final
最终的 , 不可变的
-
final修饰的类无法被继承
-
final修饰的方法不能被重写、覆盖
-
final修饰的局部变量只能赋值一次
-
final修饰的成员变量必须先赋值(构造方法/初始化赋值),值不能改变,通常用static final表示常量
static final int PI = 3.14; //内存中只有一份,所有对象共享
-
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的
final StringBuffer a=new StringBuffer("immutable"); // a=new StringBuffer(""); 不能改变其引用 a.append(" broken!"); //但可以改变对象的内容
每个对象可以自定义赋值,但值不可改变
public class FinalDemo {
final int num; //不用static修饰,先不赋值,构造方法中初始化,就可以让每个对象都包含一个num常量
public FinalDemo(int num) {
this.num = num;
}
public static void main(String[] args) {
FinalDemo finalDemo1 = new FinalDemo(0);
FinalDemo finalDemo2 = new FinalDemo(1);
System.out.println(finalDemo1.num); //每个对象可以定义num的值
System.out.println(finalDemo2.num);
}
}
-------------------------
0
1
代码块
静态代码块在类被加载时被调用,只执行一次
实例块在创建对象时调用,每创建一次对象执行一次
包
包从电脑文件系统的角度讲就是文件夹
从java程序的角度讲是类的路径.
作用
- 按照不同的功能管理类
- 避免类重名
- 访问权限控制
- java.lang包中的类在使用不需要导入
- 使用其他包中的类时,需要使用import关键字导入
访问权限修饰符
封装
封装的作用
- 防止类的属性被外部代码随意的修改和访问,保证数据的完备性
- 将对属性的操作转换为方法,更加灵活和安全
- 使用封装可以隐藏实现的细节:使用者只需要使用,不需要知道过程
- 在类的定义结构中修改,提高了代码的可维护性,同时又可以不影响外部的使用
- 通过封装方法可以有效减少耦合
耦合:模块与模块之间,代码与代码之间的关联程度,对属性封装后,和调用相关的代码就会变得相对简单,可以降低耦合
如何使用
-
使用权限修饰符
1.使用private,关闭直接访问的入口
2.使用public,提供调用的入口 set() get()
-
定义与属性存取相关的方法
就可以对存入的数据进行判断,对不符合逻辑的数据进行处理
public class Person{
// 使用private声明属性
private String name;
private double money;
// 使用public声明方法,作为操作属性的入口
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setMoney(double money){
//可以在方法中可以自定义其他逻辑
this.money = money;
}
public double getMoney(){
return this.money;
}
}
继承
继承是指在一个现有类的基础上去构建一个子类,子类会自动拥有父类所有可继承的属性和方法,并能扩展新的能力。
- 子类拥有父类中所有的属性和方法。父类中非私有的可以直接获取;私有的属性和方法子类仍能获取,只是因为封装性的影响,使得子类不能直接访问父类的结构,需要提供get(),set();父类私有的方法子类不能直接使用,只能借助方法对其进行操作。这样看子类的确用到了父类私有属性和方法,所以就认为子类继承到了。
- 子类继承父类后,还可以声明自己特有的属性和方法,实现功能的扩展
- 子类可以重写父类的方法。构造方法、静态方法不能被重写, 属性也不存在重写。
好处
- 减少了代码的冗余,提高了代码的复用性
- 提高代码的可扩展性
- 将类和类之间产生了关系,提供了多态的前提
/*
设计一个动物类 父类 基类
当一个类没有显示的继承其他类的时候,此时这个类默认继承Object
Object类是java中所有类的基类(父类 超类)
*/
//public class Animal extends Object{
public class Animal{
private String name;
private int age;
public Animal(){
super();
System.out.println("Animal无参构造");
}
void eat(){
System.out.println("动物吃东西");
}
public void sleep(){
System.out.println("动物睡觉");
}
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;
}
}
/*
设计Dog类
狗是动物 继承Animal 拥有父类中行为属性(非私有的)
java中的继承:一个类只能直接继承一个父类
Dog extends Animal 显示的继承 extends Object
*/
public class Dog extends Animal{
private String type;
//无参构造
public Dog(){
super();
System.out.println("Dog无参构造");
}
//有参构造
public Dog(String type){
super();
this.type=type;
System.out.println("Dog有参构造");
}
public void play(){
System.out.println("1111");
System.out.println(super.getName()+"狗会玩");
}
//构造方法,静态方法不能被重写, 属性也不存在重写,
//当父类中实现方式(代码) 不能满足子类中的需求,可以在子类中将父类中的方法重写(方法覆盖 覆写)
@Override//java中的注解标签 说明 此方法是从父类中重写过来的 在编译期间就会对语法进行校验
void eat() {
System.out.println("ssssss");//子类自己的实现方式
System.out.println("ssssss");
//super.eat();//调用父类的实现
super.sleep();
System.out.println("ssssss");
System.out.println("ssssss");
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class XiaoTianQuan extends Dog{
public XiaoTianQuan(){
// super();//super() 表示调用父类的构造方法
// super("牧羊犬");//调用父类的构造方法 默认调用(默认存在)
//如果使用super()显示的调用,则必须方法构造方法的第一行
System.out.println("XiaoTianQuan无参构造");
}
public void fly(){
System.out.println("哮天犬会飞");
}
}
public class Test {
public static void main(String[] args) {
Dog wc = new Dog();
wc.sleep();
wc.eat();
wc.setName("旺财");
wc.setAge(5);
System.out.println(wc.getName());
System.out.println(wc.getAge());
wc.setType("牧羊犬");
wc.play();
System.out.println(wc.getType());
wc.hashCode();
System.out.println("============================");
XiaoTianQuan xtq = new XiaoTianQuan();
xtq.hashCode();//Object
xtq.setName("哮天犬");//Animal
xtq.setAge(500);
xtq.play();//Dog
xtq.fly();//自己
}
}
public class Test1 {
public static void main(String[] args) {
/*
构造方法作用:初始化类的成员
*/
XiaoTianQuan xtq = new XiaoTianQuan();
// xtq.eat();
/*
当创建一个子类对象时,会在子类的构造方法中调用父类的构造方法,先去初始化父类.
默认会在构造方法的第一行使用super()调用
如果显示的使用使用super()调用,也必须在第一行,
可以使用super()传参,调用指定的构造方法
*/
}
}
public class Test2 {
public static void main(String[] args) {
Dog wc = new Dog();
wc.setName("旺财");
wc.play();
wc.eat(); //此时 wc 调用的是Dog类中重写的eat()
wc.sleep();
}
}
this & super
this指当前对象,代表当前对象本身
- 引用此对象的属性和方法
- 区分成员变量和形参
- 引用构造方法
class Person {
private int age = 10 ;
public Person(){
System.out.println( "初始化年龄:" +age);
}
public int GetAge( int age){
this.age = age;
return this.age;
}
}
super指当前类的父类
- 引用当前对象的父类
- 区分子类中的成员变量或方法与父类中的成员变量或方法名
- 引用构造方法
class Country {
String name;
void value() {
name = "China" ;
}
}
class City extends Country {
String name;
void value() {
name = "Shanghai" ;
super .value(); //调用父类的方法
System.out.println(name); //city的name
System.out.println( super.name); //country的name
}
引用构造方法
super(参数):调用父类中的某一个构造方法(应该为构造函数中的第一条语句)
this(参数):调用本类中另一种形式的构造方法(应该为构造函数中的第一条语句)
public class Person{
Person() {
System.out.println("父类_无参构造方法");
}
Person(String name){
System.out.println("父类_有参构造方法+name is :"+name);
}
}
public class Student extends Person{
Student(){
super(); //调用父类的无参构造方法
System.out.println("子类_无参构造方法");
}
Student(String name){
super(name); //调用父类有参的构造方法
System.out.println("子类_有参构造方法+Student.name :"+name);
}
Student(String name,int age){
this(name);
System.out.println("Student.name :"+name+"\tage :"+age);
}
}
public class Text {
public static void main(String[] args) {
Student jam = new Student();
Student kk = new Student("KK");
Student ll = new Student("LL",18);
}
-----------------------------------------------------------
父类_无参构造方法
子类_无参构造方法
父类_有参构造方法:name is :KK
子类_有参构造方法:Student.name :KK
父类_有参构造方法:name is :LL
子类_有参构造方法:Student.name :LL age :18
多态
多态就是同一种事物,在不同时刻表现不同的状态
也就是同一个接口,使用不同的实例而执行不同操作
就是父类定义的一个方法,不同的子类用不同的方式实现它,在实例对象的时候可以使用父类引用,编译时调用父类这个方法,运行时会调用子类的这个方法;但如果这个变量或者方法是静态的,编译期间和运行期间都会调用父类的。
满足3条件
- 继承
- 方法重写
- 父类引用指向子类对象
- 多态访问类中的成员方法:编译调用父类的方法,运行执行子类中的方法:俗称编译看左边,运行看右边
- 多态访问类中的静态成员和静态方法:编译和运行都看左边
public abstract class Animal {
void sleep(){
System.out.println("动物睡觉");
}
}
public class Dog extends Animal{
@Override
void sleep() {
System.out.println("狗睡觉");
}
}
public class Cat extends Animal{
@Override
void sleep() {
System.out.println("猫睡觉");
}
}
public class Text {
public static void main(String[] args) {
Animal animal = new Dog(); //父类的引用指向子类对象
animal.sleep();
animal = new Cat(); //同一对象的不同状态
animal.sleep();
}
}
-------------------------------------------
狗睡觉
猫睡觉
优点
提高程序的扩展性,比如说设计一个喂动物的方法,不用多态就要写每一类的方法,使用多态:只用写一个方法,不同时刻表现不同状态
缺点
父类类型不能访问到子类中特有的方法
向下转型
instanceof:检测父类引用中表示的对象类型是否为指定的类型
/*在feed()中加入Dog中特有的play()*/
public void feed(Animal animal){
animal.eat();
if(animal instanceof Dog){ //检测父类引用中表示的对象类型是否为指定的类型
Dog dog = (Dog) animal; //否则Cat类型无法转换成Dog类型
dog.play();
}
}
Java中的多态表现
- List<> list = new ArrayList();
- ApplicationContext app = new ClassPathXmlApplicationContext ();
抽象类&接口
抽象类
如果一个类中没有包含足够的信息(抽象方法)来描绘一个具体的对象,这样的类就是抽象类,一般用在体系结构的顶层用来进行功能的定义声明
抽象方法:只有方法声明, 没有方法体,也就是没有具体的实现
一个类如果继承了抽象类,要么重写抽象类中所有的抽象方法,要么将该类也定义为抽象类
- 抽象类中可以没有抽象方法,有抽象方法必定是抽象类
- 抽象类可以拥有构造方法,但是不能创建对象
public abstract class Animal {
int num;
public Animal(){
System.out.println("Animal无参构造");
}
public abstract void eat();//在比较顶级的类中就可以定义一个方法即可
public void sleeep(){
System.out.println("动物睡觉");
}
}
//public abstract class Dog extends Animal {
public class Dog extends Animal {
public Dog(){
super();
}
@Override
public void eat() {
System.out.println("狗吃东西");
}
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.num);
dog.eat();
dog.sleeep();
}
}
----------------------------------------------------
Animal无参构造
0
狗吃东西
动物睡觉
接口
是抽象方法的集合,接口相当于制定了一个规范,指明了一个类需要做什么,如果一个类实现了某个接口,就要重写这个接口的所有方法
接口中可以有属性:默认是由 public static final 修饰的
接口中可以有方法:都是抽象的且默认 public 修饰的
弥补了java单继承性
interface Person { //person接口
void eat(); //有eat的功能
int num = 0; //但是在接口中存在的变量一定是final,public,static的
}
public class Student implements Person{
@Override
public void eat() { //实现eat功能
System.out.println("学生吃");
}
}
public class Teacher implements Person{
@Override
public void eat() {
System.out.println("老师吃");
}
}
public class Text {
public static void main(String[] args) {
Student student = new Student();
student.eat();
Person s = new Student();
s.eat();
s = new Teacher();
s.eat();
System.out.println(Person.num); //用接口调用,不能用实例对象调用
}
}
-------------------------------
学生吃
学生吃
老师吃
0
对比
使用场景
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口
比如说表示男人、女人就使用抽象类,表示吃、睡这个动作就使用接口
- 多继承,使用接口
- 基本功能不断改变,使用抽象类,如果使用接口,就要改变实现接口的所有类
区别
- 抽象类是对根源的抽象,表示这个对象是什么;接口是对动作的抽象,表示这个对象能做什么
- 一个类只能继承一个抽象类,但能实现多个接口
- 接口只能定义方法不能实现,抽象类可以实现部分方法
- 接口中的属性默认是public static final修饰的,方法是pubilc 修饰的,抽象类中抽象方法必须用abstract修饰的
- 抽象类用于代码复用,接口用于抽象事物的特性