面向对象编程
类和对象
类的创建
语法
[修饰符] class 类名{类体:包含各种成员}
类名采用驼峰命名法
- 类对象的创建
类名 变量名=new 类名();
- 访问对象中的字段和方法
对象.字段
对象.方法
this关键字
- this关键字 可以理解成 我 , 本身的含义 指代 当前对象 。
- this关键字可用来引用当前类的 实例变量 。 谁调用就是谁
- this关键字可用于调用 当前类中实例方法(隐式)。 在实例方法中,可以直接调用 其他的实例方法,而不用书写this关键字 , 会自动加上, 但是建议书写!
- this()可以用来调用当前类的 构造函数。
- this关键字可作为调用实例方法中的 参数传递。 调用实例方法的时候,可以将 this 作为参数进行传递, 此时的this 依旧是 当前对象。
- this关键字可作为参数在构造函数调用中传递。 此时 this 依旧是一个 当前对象
- this关键字可用于从方法返回当前类的实例。 在方法中 , 使用 this 作为一个返回 。返回当前对象,谁调用就返回谁!
内存分析之创建对象
示例
public class Person {
int age;
String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public static void main(String[] args) {
Person p=new Person(12,"张三丰");
}
}
当执行 Person p=new Person ; 的时候,首先会在 栈(stack) 中 声明 Person类型的变量 p , 然后在堆(heap)中开辟空间【是new 的作用】 ,会为 Person 类中的字段(Field)设置默认值(前提是没有进行指定值) 并 执行Person() 中的代码(构造器中的代码),最后通过 = 将 堆中的地址 复制给p 变量。
static 修饰的内容会被放在方法区(Method area)中
方法
语法
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
示例
public class MethodUse {
public void method(){
System.out.println("这就是方法的定义!!!");
}
public static void main(String[] args) {
MethodUse methodUse=new MethodUse();
methodUse.method();
}
}
注意:方法内部不能再定义方法,但是可以调用方法。如果没有返回值,则要写上void。返回值类型可以是基本数据类型,也可以是引用数据类型。
形参和实参
- 形参:形式参数,在方法声明时,()中的参数。
- 实参:实际参数,在方法调用时直接传入的参数。
方法的属主
static修饰的方法应该用类名来调用。
非static修饰的方法(实例方法)应该用对象来调用。
示例
public class MethodOwner {
int age;
String name;
public MethodOwner(int age, String name) {
this.age = age;
this.name = name;
}
public void show(){
System.out.println(this.name+this.age);
}
public static void sleep(){
System.out.println("开始睡觉了!!!");
}
public static void main(String[] args) {
MethodOwner methodOwner=new MethodOwner(12,"张三丰");
methodOwner.show();
//static修饰的方法只能通过类名来调用
//methodOwner.sleep();
MethodOwner.sleep();
}
}
方法重载
同一个类中有两个或两个以上方法同名不同参的方法,称为方法重载(满足同名不同参)
特点:
- 同一个类 , 如果存在继承,那么需要保证 继承来的方法 与 本类中自己的方法 同名不同参
- 同名 : 方法名相同
- 不同参 : 参数 个数、顺序、类型 不一样
- 返回值 与 修饰符 不做要求 。
示例
//父类
public class OverLoad {
public void add(String a,int b){
System.out.println(a+b);
}
}
//子类
public class OverLoadInherit extends OverLoad{
//参数类型不同
public double add(double a, double b) {
return a+b;
}
//参数个数不同
public int add(int a){
return a;
}
//参数顺序不同
public void add(int a,String b){
System.out.println(a+b);
}
}
可变长参数
参数在 方法调用的时候出现 ; 而参数的个数,相同类型可以有多个 , 但是 可以不用在 方法调用时 传递那么多的参数。
语法
数据类型...形参名字
示例
public void foo(String...args)
注意:
- 可变参数只能作为函数的最后一个参数,但其前面可以有也可以没有任何其他参数
- 由于可变参数必须是最后一个参数,所以一个函数最多只能有一个可变参数
- Java的可变参数,会被编译器转型为一个数组
- 变长参数在编译为字节码后,在方法签名中就是以数组形态出现的。这两个方法的签名是一致的,不能作为方法的重载。如果同时出现,是不能编译通过的。可变参数可以兼容数组,反之则不成立
示例
public class VariableArgs {
String name;
public void show(String...names){
for(String name:names){
System.out.println(name);
}
}
public static void main(String[] args) {
VariableArgs variableArgs=new VariableArgs();
variableArgs.show("张三丰","张无忌","令狐冲");
//直接传递数组也行
variableArgs.show(new String[]{"张三丰","张无忌","令狐冲"});
}
}
参数传递机制
在Java 中, 只存在一种 参数传递机制 : 值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
- 基本数据类型 : 将实参基本数据类型变量的“数据值”传递给形参
- 引用数据类型 : 将实参 引用数据类型变量 的 ”地址值“ 传递给形参
封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
通过 权限修饰符 可以将 一些内容 藏起来 . 注意:权限修饰符一共有 3个, 但是 访问级别有 4 个 .
- 权限修饰符 : private ( 私有的 ) , protected( 受保护的 ) , public( 公开的 )
- 访问级别 : private , 缺省的( 来自于C++ , C++ 中关键字为default , 在Java 中不存在对应的关键字) 或 默认的, protected , public
访问级别表:
封装的实现
示例
public class Person {
private int age;
String name;
protected String sno;
public double weight;
//使用public方法来对私有自动进行获取
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
public class TestPerson {
public static void main(String[] args) {
Person p=new Person();
//私有字段只能在其类中访问
//p.age=12;
p.name="张三丰";
p.sno="1212";
p.weight=213123;
//通过共有方法访问私有字段
p.setAge(12);
p.getAge();
}
}
- 构造
构造 又称 构造方法、构造器 (Constructor) 、构造函数 。是一个类里用于创建对象的特殊子程序 。
构造就是为了 构建一个对象 而存在 。 并且 经常接收一些参数,用来为 实例变量(非static 修饰的 字段 ) 赋值 。
语法
【修饰符】 类名( 【形参】 ){ 方法体 ;}
如果一个构造方法 没有任何参数,此时可以被称为 无参构造 。
构造方法与普通方法之间的区别
- 构造方法没有任何返回值 , 也就是说没有返回类型; 普通方法存在 返回类型
- 构造方法的名称 是 类名 , 如果不是类名就会变成一个普通的方法; 普通方法的方法名 是可以随便书写。
构造之间的重载 - 所有的构造方法的名字都是 类名 。 —— 同名
- 构造方法中的 参数的 个数、类型、顺序 不同 。—— 不同参
- 对修饰符没有任何要求 。
默认构造 : 当创建一个类的时候, 如果没有明确书写构造方法,则 JDK 会自动加上一个 public 修饰 无参构造 。但是,如果一旦书写了任何一种构造方法,那么JDK 就不会再次提供 构造方法了。所以, 在添加构造方法的时候,一般 需要提供一个 public 修饰的无参构造。
构造方法本身没有任何返回 , 但是 new + 构造方法 有返回值 .
示例
public class Animal {
private String name;
private int age;
public Animal(){
System.out.println("无参构造执行了!!!");
}
public Animal(String name){
this(name,12);
System.out.println("有参构造执行了!!!");
}
//构造方法重载
public Animal (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;
}
public static void main(String[] args) {
Animal animal1=new Animal();
Animal animal=new Animal("Dog");
}
}
继承
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
子类可以继承父类所有的字段和方法,但是能不能访问由访问权限来决定。
java.lang.object是所有类的父类。java只能单继承。
示例
//父类
public class Animal {
private String name;
private double weight;
public Animal() {
}
public Animal(String name, double weight) {
this.name = name;
this.weight = weight;
}
public void eat(){
System.out.println("我的名字是:"+this.name+",我要吃饭了");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
//子类
public class Dog extends Animal{
//子类可以新增内容
public void sleep(){
System.out.println("开始睡觉!!!");
}
}
//主函数测试
public class Test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.setName("poppy");
dog.setWeight(20);
String name = dog.getName();
System.out.println(name);
dog.eat();
dog.sleep();
}
}
super 关键字
this 表示我, super 表示 我父亲
super关键字是一个引用变量,用于引用直接父类对象 . super 关键字 可以代表 父类的一些东西 .
用法 :
- 可以表示父类的 实例对象 . 表示父对象 . 注意:有可能是 直接父级对象 或 间接父级对象 .
- super() ; 表示调用父类的构造方法 . 在 子类中的构造中使用 , 是放在 子类构造中的第一行! 如果没有书写,则JDK 会 隐式(偷偷摸摸的) 添加这一行
示例
//父类
public class Animal {
private String name;
private int id;
public Animal(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
//子类
public class Mouse extends Animal {
public Mouse(String myName, int myid) {
super(myName, myid);
}
}
重写(override)
重写( override )与重载( overload )的异同
重写 : 同名( 方法名相同 ) 同参( 参数相同 ) 同返回( 返回类型相同 )
- 发生在 继承 关系中
- 方法名相同 , 参数 相同 ( 参数的个数 类型 顺序 )
- 返回类型 :
- 基本数据类型 : 完全一致
- 引用数据类型 : 保证 “同源”
- 修饰符 : 子类重写方法的修饰符 不能 比 父类方法中的修饰符 要小 ( 访问权限 )
示例
//父类
class Animal{
public void move(){
System.out.println("动物可以移动");
}
}
//子类
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
}
//主函数测试
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
}
}
多态
多态是 同一个行为 具有多个 不同表现形式或形态 的能力。
分类
- 运行时多态 : 发生的基础 是继承 . 同时 保证 子类重写了 父类 的 方法. 本质是 父类类型指向子类对象 ( 父类变量指向了 子类对象 )
- 编译时多态 : 是由 重载 决定的. 在 .java 文件 编译阶段 表现出 多种形态 . 实际上 是由 参数的多样化 决定的.
运行时类型与编译时类型 : - 运行时类型 : Human h = new Chinese() ; 在 运行阶段, 引用的 是 Chinese 类型, 那么 运行时类型就是 Chinese . Human h = new Hindu() ; 在运行阶段 , 引用的是 Hindu 类型, 那么对应的运行时类型就是 Hindu.
- 编译时类型 : Human h = new Chinese() ; 此时的 h 的类型是 Human, 这个类型就是编译时类型 Human h = new Hindu() , 此时 h 的编译时类型时 Human .
instanceof
含义 : xxx 是 YYY 吗 ? 返回值是 boolean 类型 ; 判断变量的运行时类型
语法
变量 instanceof 类
示例
多态和instanceof结合
//父类
public class Animal {
public void call(){
System.out.println("动物的叫声");
}
}
//子类
public class Dog extends Animal{
@Override
public void call(){
System.out.println("狗的叫声:汪汪汪");
}
}
public class Duck extends Animal{
private String name ;
@Override
public void call(){
System.out.println("鸭子的叫声:嘎嘎嘎");
}
}
//主函数测试
public class TestPolymorphism {
public static void main(String[] args) {
show(new Dog()) ;//以Dog对象调用show方法
show(new Duck());//以Duck对象调用show方法
Animal animal = new Dog() ;//向上转型
animal.call();//调用的是Dog的call方法
Dog dog = (Dog)animal;//向下转型
dog.call();//调用的是Dog的call方法
}
public static void show(Animal animal){
animal.call();
if (animal instanceof Dog){
Dog dog = new Dog() ;
dog.call();
}else if (animal instanceof Duck){
Duck duck = new Duck() ;
duck.call();
}
}
}
引用强制类型转换的本质
Dog dog = (Dog)animal; dog并转换成目标类型(Dog)
static关键字
static 表示 静态的 , 表示 类加载 初始化阶段 , 可以修饰 变量、方法、代码块、内部类(内部接口) 。
static 修饰的内容 是通过 类名.变量(方法) 直接使用
- 修饰变量 ( 静态变量, 静态成员变量, 类初始化变量, 类初始化字段 ) , 一般是 在 类中 直接使用, 表示 该变量 在 类 初始化的时候 就已经定义好了。
- 修饰方法 ( 静态方法 ) , 一般是 在类中直接使用, 表示 该方法 在 类 初始化的时候 就已经定义好了。
- 修饰代码块 : 就可以表示 类已经被加载了, 类加载阶段就会执行的代码。
变量分类
- 变量的作用域 : 离变量最近的花括号{}。
- 成员变量
- static修饰 (静态成员变量 、类初始化成员变量 ) : 加载到 方法区中, 伴随着 类的存在而存在。
- 实例变量(没有使用 static 修饰的变量 ) : 随 对象存在而存在, 加载到 堆空间 中 , 作用范围是 整个类体括号
- 局部变量
- 在 方法中 或 代码块中定义 ; 作用范围在声明的范围之内 ; 加载到栈中
代码块
代码块与 变量的分类是差不多的。
语法:
[修饰符] {
此时就是一个代码块了!
}
- 成员代码块
- 静态代码块(类初始化代码块) 使用 static 修饰的代码块
- 实例代码块 : 没有使用 static 进行修饰的代码块
- 局部代码块 : 在 方法内部 直接 声明的代码块 , 很少使用。
执行顺序
- 一个类中存在 代码块、static代码块、构造方法 的时候, 先 static 代码块, 再 代码块, 最后是构造。
- 一个类中存在 多个 代码块 , 多个 static代码块 、 构造的时候, 先 static 代码块, 再 代码块, 最后是构造。
- 多个代码块的时候,会按照 书写顺序(从上到下)执行
- 当 static 代码块 执行的时候,就可以认为 这个类 已经被加载到 jvm 中了。
- 存在继承的时候,代码块 、 static 代码块 、 构造的执行顺序
- 父类静态代码块
- 子类静态代码块
- 父类实例代码块
- 父类构造方法
- 子类实例代码块
- 子类构造方法
示例
//父类
public class Animal {
public Animal() {
System.out.println("父类构造方法");
}
static {
System.out.println("父类静态代码块");
}
{
System.out.println("父类实例代码块");
}
}
//子类
public class Dog extends Animal{
public Dog() {
System.out.println("子类构造");
}
static {
System.out.println("子类静态代码块");
}
{
System.out.println("子类实例代码块");
}
}
//主函数测试
public class TestStatic {
public static void main(String[] args) {
Dog d = new Dog() ;
}
}
final
final : 最后的, 最终的 , 不可改变的 。
final 可以修饰 变量 、 非抽象方法、 非抽象类 。
- final 修饰的变量 不可改变
- final 修饰的方法 不可 重写
- final 修饰的类 不可 继承
示例
//示例1
//final修饰的字段
public class User {
//用户身份证号
//final修饰的就是不可更改的量
private final String ID ;
public User(String ID) {
this.ID = ID;
//一旦重新赋值,编译失败
//this.ID = "111111111111111111111";
}
public static void main(String[] args) {
User user =new User("123");
System.out.println(user.ID);
User u = new User("456");
System.out.println(u.ID);
}
}
//示例2
//final修饰的方法
public class Animal {
public final void call(){
System.out.println("动物的叫声");
}
}
public class Dog extends Animal {
//无法重写
//@Override
//public final void call(){
//System.out.println("狗的叫声:汪汪汪");
//}
}