目录
类的概念
- 1、类似抽象的,概念的,代表的是一类事物(人类、猫类、食物类……)它是数据类型、
2、对象是具体的,实际的、代表的是一个具体事物,是实例
3、类是对象的模版,对象是类的一个个体,对应一个实例 属性(成员变量) field(字段)
如
class Cat{
String name;//属性
int age;//属性
}
先声明在创建
Cat cat;
cat = new Cat();
类的内存分配机制
_
class Person{
String name;//属性
int age;//属性
}
- 1、先加载Person类信息(属性和方法信息,只加载一次)
- 2、在堆中分配空间,进行默认初始化
- 3、把地址赋给P,P就指向对象
4、进行指定初始化(p.name = “jack”,p.age = 10;) 成员方法的定义
访问修饰符 返回数据类型 方法名 (形参列表){
//方法体
//return 返回值 }
访问修饰符(四种)
public(公共的)
private(私有的)
protectde(受保护的)
默认(不写)
- 1、一个方法最多只有一个返回值,【返回多个值使用数组】
- 2、返回类型可以为任何类型,包括基本类型和引用类
- 3、如果方法要求有返回数据类型,则方法体中最后的执行语句必须是return,而且要返回值类型和return的值的类型必须兼容或相同
- 4、方法名:遵循驼峰命名法, 见名知意
1、 public 公开级别,对外公开
2、protected 受保护级别,对子类和同一个包中的类公开
3、private 私有级别 只有本类本身可访问,不对外公开
4、 空) 默认级别 ,没有访问修饰符,向同一个包的类公开
!!!:类只有public 和 默认可以修饰!
方法调用:
1、同一个类中方法的调用;直接调用
2、跨类:A方法调用B方法必须先创建B对象在调用
方法传参机制
parameter (参数)
1、每个方法在栈中都有一个独立的空间,方法之间互不打扰
2、基本数据类型传递的是值,形参的改变不影响实参,
3、引用类型传递的是地址,可以通过形参影响实参
编写方法的思路
1、方法的返回类型
2、方法的名字
3、方法的形参
4、方法体
每调用一个方法就会产生一个新栈
方法重载
介绍:JAVA中允许同一个类中出现多个同名方法的存在,但要求形参列表不同
注意事项:
1、方法名必须一样
2、形参列表不一致(形参类型或个数或顺序至少有一样不同,参数名无要求)
3、返回类型无要求(返回类型不是构成重载的条件)
可变参数
JAVA允许将同一个类中多个同名同功能但参数个数不同的方法封装成一个方法
基本语法:
访问修饰符 返回类型 方法名 (数据类型 … 形参名)
public int sum (int … nums){
System .out.println (“接受的参数的个数”+ nums.length);
int res = 0;
for(int i = o;i<nums.length;i++){
res+=nums[i];
}
return res;
}
注意:
1、int…表示接受的是可变参数,类型是int 可以接受0到多个int
2、使用可变数组时,可以当作数组来使用,即num可以当作数组来使用
3、遍历nums求和(遍历数组)
可变参数细节:
可变参数的元素(实参)可以是0到多个
可变参数的本质就是数组
可变参数可以和普通类型参数放在一起但要保证可变参数在最后面
形参列表中值能出现一个可变参数
作用域;
基本使用:
1、面向对象中,主要的变量是属性(成员变量)和局部变量
2、我们说的局部变量一般指的是在成员方法中定义的变量
JAVA中作用域的分类:
1、全局变量:也就是属性,作用域为整个类 和类中的方法
2、局部变量:除了属性之外的其他变量,作用域为定义它的代码块中
3、全局变量可以不赋值,直接使用,因为有默认值,局部变量需要赋值
注意事项:
属性和局部变量可以重名,访问时就近访问
全局变量/属性:可以被本类使用或其它类使用
局部变量:只能在本类中对应的方法使用
全局变量可以加访问修饰符
局部变量不可以加访问修饰符
构造方法/构造器(conlstructor)
基本语法:
访问修饰符 方法名 (形参列表){
方法体 }
- 构造器的修饰符可以默认也可以是 public private protected
- 构造器没有返回值 方法名和类名必须一样
- 参数列表和成员方法一样的规则 构造器的调用系统完成
基本介绍
- constructor : 构造方法(构造器)是类的特殊方法,它的主要作用是完成对新对象的初始化 方法名和类名相同 没有返回值
- 在创建对象时,系统会自动调用该类的构造器完成对 对象的的初始化
用法:
public Class {
public static void main(String [] args){
Person p1 = new Person("jack",80);
}
}
Class Preson{
String name ;
int age;
public Person (Steing pname int page ){
name = pname;
page = age;
}
}
自己没有定义构造器时,系统会自动生成”默认的无参构造器“,一旦自己定义自己的构造器,默认的构造器就会被覆盖!
包:
包的三大作用:
- 区分相同名字的类
- 当类很多时可以很好的管理类
- 控制访问范围
包的基本语法:
package com.hhpud(自己定)
说明: package 关键字。表示将类打包在com.hhpdu包中
com.hhpud 表示包名
包的本质实际上就是创建不同的文件夹/目录来保存文件
包的命名:
包只能包含数字,字母,下划线。小圆点(.)但不能用数字开头,不能是关键字或者是保留字
面向对象编程的的三大特征
基本介绍
面向对象编程有三大特征: 封装 继承 多态
封装(encapsulation):
封装的通俗例子(电视机,把电视机的所有零部件装在一个小盒子里,用户不用去管怎样运行的,也不可以随意修改里面的东西(除非有某种特权))在编程里
把所有方法和类封装起来必须获得某种权限才可修改、
封装的实现步骤
- 将属性私有化(private) 【作用是不能再外直接修改属性】
- 提供一个公共(public)的set方法,,用于对属性判断并赋值
public void set***(类型 参数名){//***表示某个属性
//可加入数据验证的业务逻辑
属性 = 参数名; }
提供一个公共的(public ) 的get方法,用于获取属性的值
public 类型 get***(){
//可加入获取这个属性的权限业务逻辑
return ** }
继承定义:
继承可以解决代码复用,让我们的编程更加靠近人的思维,当多个类存在相同的属性和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不用重新定义这些属性和方法,只需通过extends 来表明继承父类即可
继承的基本语法:
class 子类 extends 父类 {
}
子类会自动拥有父类定义的属性和方法
- 父类又叫超类或基类 子类又叫派生类
- 子类继承了父类所有的属性和方法,非私有的属性和方法可
- 以在子类直接访问,但是私有的属性和方法不能再子类中直接访问,可以通过父类提供的公共的方法去访问
继承细节:
- 子类必须调用父类的构造器完成对父类的初始化
- 当创建子类对像时,不管子类使用的那个构造器,默认情况下总会去调用父类的无参构造器,
- 如果父类没有提供无参构造器,则必须在子类的构造器中使用super去指定使用父类的那个构造器完成对父类的初始
- 如果希望指定去调用父类的某个构造器,则显示的调用一下:super(……);
- super在使用时,必须放在构造器的第一行(super();只能在构造器中使用) ;
- super()和this()都只能放在构造器的第一行,因此这两个方法不能同时出现在一个构造器中
super关键字
super表示父类的引用,用于访问父类的属性,方法,构造器
基本语法:
1、访问父类的属性,但不能访问private的属性(super.属性名);
2、访问父类的方法,不能访问private的方法;(super.方法名(参数列表))
3、访问父类的构造器, super (参数列表)只能放在构造器的第一句,并且只能出现一次
(super this 直接访问规则):
- B extends A A中有一个Cal方法 子类B中无Cal方法 找Cal方法时 (Cal( ); 和 this .Cal( );):
- 先找本类(B)如果有 ,调用 没有;则找父类(A)(如果有 并且可以调用 ,则调用)
- 如果父类没有,则继续找父类的父类直到object类 用super 访问时 B类中有Cal方法,A类也有Cal方法(A是B的父类)
- super.cal();--------->输出A中Cal方法中的东西
因为:super在子类中访问某个方法时,是直接从该子类的父类中访问的 向上找 知道 object
当子类中有和父类中的成员(属性和方法)重名时,为了访问父类中的成员,必须通过super访问,如果没有重名,使用super和this访问是一样的效果(this是就近访问)
[super和this的比较】
区别点 this super
1 访问属性 访问本类中的属性,如果本类中没有就去父类找 访问父类中的属性
2 调用方法 访问本类中的方法,如果没有就去父类找 直接访问父类中的方法
3 调用构造器 调用本类中的构造器,比需放在构造器的首行 调用父类构造器,必须放在子类构造器的首行
4 特殊 表示当前对象 子类中访问父类对像](https://img-blog.csdnimg.cn/33fc54fafb4a413e8a2c9ea92e6c0d14.png)
方法重写
介绍:
方法重写(覆盖)就是子类有一个方法,和父类的某个方法的名称,返回类型,参数一样,那么我们就说子类的这个方法重写(覆盖)了父类的方法
package com;
public class Person {//父类
private String Name;
private int age;
public Person(String Name, int age) {
this.Name = Name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return Name;
}
public void say() {//父类中的say方法
System.out.println("Preson中的自我介绍 \n " + "姓名: " + Name + " 年龄:" +
age);
}
}
package com;
public class Student extends Person {//子类,继承了Person
private String id;
private int score;
public Student(String Name, int age, String id, int score) {
super(Name, age);
this.id = id;
this.score = score;
}
@Override
public void say() {//重写Person中的say方法
System.out.println("Student中的自我介绍\n姓名:" + getName() + " 年龄:" +
getAge() + " 成绩:" + score + " 个人ID:" + id);
}
}
多态:
养宠物引出多态;
用封装,继承不利于多种动物的饲养,(多个对象的创建)导致代码复用性不高,不利于维护
解决方案:引出多态
一个对象的编译类型和运行类型可以不一样
如:Animal animal = new Cat();(等号左边为编译类型,右边为运行类型)
编译类型在定义对象是,就确定了不可更改;
运行类型是可以变化的,
多态的注意事项和细节
多态的前提:两个对象(类)之间是继承关系
多态向上转型:
本质:父类的引用类型指向子类的对象
语法:父类类型 引用名 = new 子类类型();
特点:编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(遵循访问权限) 不能调用子类中特有的成员
- 最终运行效果看子类的具体实现(即调用方法时,按照从子类(运行类型)开始查找方法,然后滴用) 向上转型调用方法需要有方法的重写
多态向下转型:
- 假如类Cat中有一个特有的方法,我想用编译(父类)类型调用,
- 这时因为特有方法没有重写所以用编译(父类)类型调用不到,所以就要用到多态的向下转型
语法:
子类类型 引用名 = (子类类型) 父类引用
要求父类的引用必须指向的是目标类型的对象
如:
Animal animal = new Cat();
Animal是Cat的父类,
用向下转型时:Cat cat = (Cat)animal;
不能 Dog dog = (Gog)animal//这是错的,因为向上转型父类指向的是Cat
属性没有重写之说!属性的值直接看编译类型
instanceof 比较操作符
用于判断对象的运行类型是否为XX类型或者XX类型的子类型
动态绑定机制
当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定 运行类型中没有的话就去编译类型所在类中查找
当调用属性对象时,没有动态绑定机制,哪里声明,哪里使用(运行到哪里就用哪里的属性)
多态数组
以下是代码:
import com.Person;
import com.Student;
import com.Techer;
public class Main {
public static void main(String[] args) {
// write your code here
Person[] person = new Person[5];
person[0] = new Person("jack", 20);
//向上转型。。父类的引用指向子类的方法
person[1] = new Student("Tom", 18, 99);
person[2] = new Student("marry", 17, 56);
person[3] = new Techer("smith", 45, 12000);
person[4] = new Techer("wude", 48, 20000);
//遍历动态数组并输出
for (int i = 0; i < person.length; i++) {
System.out.println(person[i].say());
//因为多态编译类型不能访问子类的特有方法
//所以需要向下转型
if (person[i] instanceof Student) {//如果哪一个数组元素的运行
// 类型是Student,
//向下转型、、、访问特有方法
((Student)person[i]).studay("语文");
}else if(person[i] instanceof Techer){
((Techer)person[i]).teach ("语文");
}else if((person[i] instanceof Person)){
}else{
System.out.println("类型有错误");
}
}
}
}
package com;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public String say() {
return " 姓名 " + name + " 年龄 " + age;
}
}
package com;
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
@Override
public String say() {
return super.say() + "成绩" + score;
}
//创建子类特有方法
//课程
public void studay(String subject) {
System.out.println(getName() + " 正在上 " + subject);
}
}
package com;
public class Techer extends Person {
private int salary;
public Techer(String name, int age, int salary) {
super(name, age);
this.salary = salary;
}
@Override
public String say() {
return super.say() + " 薪水是 " + salary;
}
public void teach(String object) {
System.out.println(getName() + "正在教授" + object);
}
}
多态参数
public class Main {
public static void main(String[] args) {
// write your code here
Manager jack = new Manager("jack", 1500, 10000);
Woreker Tom = new Woreker("Tom", 5000);
Test test = new Test();
System.out.println("=======jack年总工资=====");
test.showEMployee(jack);//传入员工类型,是运行类型
System.out.println("=======Tom年总工资=====");
test.showEMployee(Tom);//传入员工类型,是运行类型
test.work(jack);
test.work(Tom);
}
}
public class Test {//测试类
public void showEMployee(EMployee e){//形参是父类类型。可指向子类,相当于 EMployee e = new Manager()
System.out.println(e.getAnnual());
}
public void work(EMployee e){
if(e instanceof Manager){//判断传入的对象是否是Manager
//向下转型访问特有方法
//因为父类中没有重写子类中的特有方法,直接用父类访问会访问不到。
// 所以要将父类向下转型用来访问子类中特有的方法
System.out.println(((Manager) e).manage());
}else if(e instanceof Woreker){
//向下转型,访问特有方法
System.out.println(((Woreker) e).work());
}else{
}
}
}
public class EMployee {
private String name;
private int salary;
public EMployee(String name, int salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public int getAnnual (){
return 12* salary;
}
}
public class Manager extends EMployee {
private int bonus ;
public Manager(String name, int salary, int bonus) {
super(name, salary);
this.bonus = bonus;
}
public String manage(){
return getName()+"是经理,正在管理";
}
public int getAnnual (){
return super.getAnnual()+bonus;
}
}
public class Woreker extends EMployee {
public Woreker(String name, int salary) {
super(name, salary);
}
public String work() {
return getName() + "是普通员工,正在打工";
}
}
Object 类详解
equals方法
"=="和equals的区别
- “= = ”和equals的对比 ”= = ”是一个比较运算符 ”= =”既可以判断基本类型,又可以判断引用类型
- ”==”如果判断基本类型,判断的是值是否相等(如: int=10;double d=10)
- ”==”如果判断引用类型,判断的是地址是否相同,即判断是不是同一个对象
A a = new A();
A b = a;
A c = b;
System.out.println(a==c);//true
System.out.println(b==c);//true
B bb = a;
System.out.println(bb==c);//true
int num1 =10;
double num2 = 10.0;
System.out.println(num1==num2);//ture
equals : 是object类中的方法,只能判断引用用类型,
默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等【看看JDK中Intrger和Object的equals源代码】
Object 中源码:
public boolean equals (Object obj){
return (this == obj );
}
Integer中equals重写源码:
public boolean equals (Object obj){
if(obj instanceof Intger){ return value == ((Integer)obj).intvalue(); }
}
重写自己的equals:
public class Person {
String name;
int age;
String gender;
public boolean equals(Object obj) {//向上转型。
if (this == obj) {//判断是否属于同一个对象
return true;
}
if (obj instanceof Person) {//判断传入的对象(运行类型)是否属于本类
Person p = (Person) obj;//向下转型,(因为编译类型[Object没有Person的
//特有属性【name age gender】])
//比较属性是否相同
return this.name.equals(p.name)
&& this.age == p.age && this.gender.equals(p.gender);
}
return false;
}
}
例题:
int it = 65;
System.out.println(“65和65.0f是否相等?”+(it == fl);//true char ch1 =‘A’;
char ch2 = 12; System.out.println(“65和‘A是否相等?”+(it == ch1));//trueSystem.out.println(“12和ch2是否相等?”+(12 == ch2)); //true
String str1= new String(“hello”);
String str2 = new String(“hello”);
System.out.println(“str1和str2是否相等?”
+(str1 == str2)); //flase,因为创建了不同的String对象(==比较的是地址)System.out.println("str!是否equals str2?”+(str1.equals(str2)));
//ture因为创建了String对象,//String中有equals方法的重写,Person p1= new Person();
p1.name =“hspedu”;
Person p2 = new Person():
p2.name ="hspedu”;
System.out.println(p1==p2);,//False,new了两个对象,p1 p2,他们的地址不同
System.out.printiln(p1.name,equals( p2.name);//T
,两个p调用的都是String类型等于是用到String()重写的equals方法比较,System.out,println(p1.equals(p2);//False,不同对象
String s1 = new String(“asdf”);
String s2 = new String(“asdf”);
System.out,printhn(si.equals(s2));/T,new了String,用的是String()重写的equals
System.out,printin(s1==s2); //F,不同对象,地址不同;
class Person{//类
public String name;>
}
hashCode方法
提高具有哈希结构的容器的效率
两个引用,如果指向同一个对象,则哈希值肯定一样
两个引用,如果指向的是不同的对对象,则哈希值是不一样的
哈希值主要根据地址号来的!,不能完全将哈希值等价于地址
在集合中hashCode 有需要的话还会重写
toString
基本介绍:
默认返回: 全类名 + @ + 哈希值的十六进制,【查看Object的toString方法】
子类往往重写toString方法,用于返回对像的属性信息
重写toString 方法: 打印对像或拼接对象时,都会调用该对象的toString形式
、、、可使用快捷键(Alt+ins)
public String toString(){//重写后,一般是把对像的属性值输出,当然程序员可以字定制
return //属性信息
}
直接输出一个对象时,toString方法会被默认的调用,比如:
【System.out.println(monster)】;就会默认调用monster.toString();
效果:
finzlize方法:
当对象被回收时,系统会自动调用该对象的finzlize方法。子类可以重写该方法,做一些释放资源的操作
演示:
什么时候或被回收:
-
当某个对象没有任何引用时,JVM(JAVA虚拟机)就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁对象前,会先调用finzlize方法
垃圾回收机制的调用,是由系统决定的(有自己的GC算法),也可通过System.gc();来主动触发垃圾回收机制
在实际开发中,几乎不会运用finalize,更多的是为了应付面试