文章目录
类与对象
属性细节
- 属性的语法:访问修饰符 属性类型 属性名
- 属性的定义类型可以为任意数据类型。
- 属性不赋值的情况下,有默认值,规则和数组相同。
如何创建对象
- 先声明再创建
Cat cat
cat = new Cat();
- 直接创建
Cat cat = new Cat();
对象分配机制
栈:一般存放基本数据类型(局部变量)
堆:存放对象 方法区:
常量池(常量、字符串),类加载信息
方法调用机制
方法中的
res
被返回后,getSum
栈即被销毁
方法使用细节
- 方法中有返回类型,返回值类型必须要和return 返回的值类型相同或者兼容【既可以进行自动类型转换】。
- 如果方法是
void
,则方法体中可以没用return
语句,或者只写return
。 - 成员方法的参数可以为任意数据类型【基本数据类型+引用数据类型】。
- 方法体里不能再定义方法。
- 同一个类中的方法直接调用,不需创建对象。而跨类调用则需要先创建一个想要调用的那个类的对象。
方法传参机制
- 基本数据类型的传参机制 ----->值传递,形参不能影响实参
- 引用数据类型的传参机制 ------->地址传递,形参可以影响实参
对象、数组均属于引用数据类型。
递归执行机制
- 每执行一个方法,就会创建一个新的受保护的空间(栈空间)。
- 方法中的局部变量是独立的。
- 如果方法中使用的是引用数据类型(数组、对象等),就会共享该引用数据的数据。
方法重载
- Java中允许同一个类,多个同名方法的存在,但要求形参列表不一致。
- 方法重载:方法名必须相同,参数必须不同(类型或个数或者顺序至少有一个不同),返回值无要求。
例1:
OverLoadExercise01.java
public class OverLoadExercise01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Method method = new Method();
method.m(3);
method.m(3,4);
method.m("hello world");
}
}
class Method {
public void m(int n) {
System.out.println(n*n);
}
public void m(int n , int m) {
System.out.println(m*n);
}
public void m(String str) {
System.out.println(str);
}
}
例2:
OverLoadExercise02.java
public class OverLoadExercise02 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Method1 method = new Method1();
System.out.println(method.max(3,4));
System.out.println(method.max(3.0,4.0));
System.out.println(method.max(3.3,4.4,5.5));
}
}
class Method1 {
public int max(int n1,int n2) {
return n1>n2?n1:n2;
}
public double max(double n1 , double n2) {
return n1>n2?n1:n2;
}
public double max(double n1, double n2, double n3) {
double max1 = n1>n2?n1:n2;
return max1>n3?max1:n3;
}
}
可变参数
- Java允许将同一个类中多个同名同功能但参数个数不同的方法封装成一个方法。
int...
表示接收可变参数- 代码示例:
//计算两个数的和、三个数的和……
public class VarParameter01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Method3 method = new Method3();
method.nums(1,5,10);
System.out.println(method.sum(1,5,10));
}
}
class Method3 {
//int...表示接收可变参数,使用可变参数时,可以当作数组使用,如nums可以当作数组
public void nums (int... nums) {
System.out.println("接收的参数个数=" + nums.length);
}
//遍历nums求和即可
int res = 0;
public int sum(int... nums) {
for(int i=0;i < nums.length ; i++) {
res += nums[i];
}
return res;
}
}
- 可变参数的实参可以为0或任意个,实参也可为数组。
- 可变参数的本质就是数组。
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后。
public void f2(String str,double… nums)
- 一个形参列表中只能出现一个可变参数。
作用域(*****)
- Java编程中主要的变量是属性(成员变量)和属性变量。
- 局部变量一般是指在成员方法中定义的变量。
- 全局变量也就是属性,作用域为整个类体。
- 全局变量(属性)可以不赋值直接使用,因为有默认值。局部变量必须要赋值才能使用,因为有默认值。
- 属性和局部变量可以重名,访问时采取就近原则。
- 同一个作用域,局部变量不可重名。
- 全局变量可以被本类使用,也可以被其他类使用【通过调用对象即类名,使用对象.变量】。
- 全局变量(属性)可以加修饰符,而局部变量不可以加修饰符。
构造器(构造方法)
基本语法
类的一种特殊方法,主要作用是完成新对象的初始化。
修饰名 方法名 (形参列表){
方法体;
}
(1)构造器的修饰名可省,也可其他。
(2) 构造器没有返回值。
(3)方法名和类名必须相同。
(4)参数列表和成员方法一样的规则。
(5)构造器的调用由系统完成。
(6)创建对象时,系统自动调用该类的构造器完成对象的初始化。
使用细节
- 一个类可以定义多个不同的构造器,即构造器重载。
- 构造器是进行对象的初始化,而不是对象的创建。
- 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造。
- 一旦定义了自己的构造器,默认构造就会被覆盖,就不能再使用,除非显式定义。
- 通过class文件的字节码进行反编译来查看Java文件中的类的源代码。
javap的使用
Javap dog.class 【class可写可不写】
真正的对象在堆中,栈中只是对对象的引用。
流程分析:
- 加载Person类信息(Person.class), 只会加载一次。
- 在堆中分配空间(地址)。
- 完成对象的初始化
- 把对象在堆中的地址返回给p(p是对象名,也可以理解为对象的引用)。
this
赵本山与奥尼尔的故事
- 哪个对象调用,this就代表哪个对象。
- this关键字可以访问本类的方法、属性、构造器。
- this可区分当前类的属性和局部变量。
- 访问成员方法的语法:
this.方法名(参数列表)
- 访问构造器的语法:
this(参数列表)
【只能在构造器中使用 即只能在构造器中访问另一个构造器,必须放在第一条语句】。 - this不能在类定义的外部使用,只能在类定义的方法中使用。
/*
* 判断一个人的名字和年龄是否和另一个人相等
*/
public class TestThis {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person4 p1 = new Person4("mary" , 20);
Person4 p2 = new Person4("smith" , 20);
System.out.println(p1.compareTo(p2));
}
}
class Person4 {
String name;
int age;
public Person4() {}
public Person4(String name,int age) {
this.name = name;
this.age = age;
}
public boolean compareTo(Person4 p) {
return this.name.equals(p.name) && this.equals(p.age);
}
}
包
包的本质就是创建不同的文件夹保存文件。
命名规范
- 只能包含数字、字母、下划线、小圆点,但不能以数字开头,不能是关键字或保留字。
- 一般为
com.公司名.项目名.业务模块名
常用的包
- java.lang.* //基本包,默认引入,不需要再引入
- java.util.* //系统提供的工具包,即将包所有引入
- java.util.Scanner //只是引入类Scanner
- java.net.* //网络包,网络开发
- java.awt.* //java的界面开发
使用细节
- 引入包的目的是使用该包下的类。
- 建议使用哪个类,就导入哪个类即可,不建议使用
*
方式导入。 import java.util.Arrays
,使用Array.sort()
对一个数组进行从小到大的排序。- 一个类中最多有一个package。
- import指令多句没有顺序要求。
访问修饰符
基本类别
- 公开级别:
public
,对外公开。 - 受保护级别:
protected
修饰,对子类和同一个包中的类公开。 - 默认级别:没有修饰符号,向同一个包中的类公开。
- 私有级别:用
private
修饰,只有类本身可以访问,不对外公开。
使用细节
- 四种修饰符可以用来修饰类中的属性、成员方法、类。
- 只有默认和
public
能修饰类。
面向对象的三大特征
封装
基本概念
就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作。如对电脑的操作就是典型的封装。
封装的好处
- 隐藏实现细节。
- 可以对数据进行验证,保证安全合理
封装步骤
(1)将属性私有化,不能直接修改属性。
(2)为每一个属性提供public
修饰的get
方法进行获取属性的值,不管修饰符是public
还是private
。
(3)为每一个属性提供public
修饰的set
方法进行赋值(可以进行对属性的校验),不管修饰符是public
还是private
。
封装与构造器
在封装中进行限制,可以被构造器轻松绕过去。为了使这些限制发挥作用,可以将set
方法写在构造器中。
继承
- 继承的关键字extends
- 非私有的属性和方法可以在子类中直接访问,即剩下的public、不写、protected都可以在子类中直接访问。
- 访问私有属性需要通过父类提供的公共方法来访问。
- 子类必须调用父类的构造器来完成初始化。
- 如果父类有无参构造器,则
super()
默认调用父类的无参构造器,且super()可以默认不写。 - 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器。
- 如果父类没有提供无参构造器或者存在有参构造器,则必须在子类的构造器中用
super
去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过。 - 如果希望去调用父类的某个构造器,则显示的调用一下:
super(参数)
。 super()
在使用时,必须放在第一行。super()
和this()
都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器。- Java中所有类都是
object
的子类,object
是所有类的基类。 - 子类与父类有相同的属性时,当访问子类.变量时,如果子类有该属性,则访问并返回信息;如果子类没有该属性,则就要看父类有没有这个属性,如果父类有该属性并且可以访问,则返回该信息;如果父类没有该属性,则继续往上一级找,直到
object
。
public class ExtendExercise {
public static void main(String[] args) {
// TODO Auto-generated method stub
PC pc = new PC("intel","16","500","I8H");
pc.printInfo();
}
}
class Computer {
public String CPU;
public String neicun;
public String cipan;
public Computer() {}
public Computer(String CPU,String neicun,String cipan) {
this.CPU =CPU;
this.neicun = neicun;
this.cipan = cipan;
}
public String getDetails() {
return "电脑的相关信息如下" + "CPU:" + CPU + "neicun:" + neicun
+ "cipan:" + cipan;
}
}
class PC extends Computer {
String brand;
public PC() {}
//这里体现出继承设计的基本思想:
//父类的构造器完成父类属性初始化,子类构造器完成子类的初始化
public PC(String CPU,String neicun,String cipan,String brand) {
super(CPU,neicun,cipan);
this.brand = brand;
}
public void printInfo() {
System.out.println( getDetails() + "brand:" + brand);
}
}
super
super
代表父类的引用,用于访问父类的属性、方法、构造器。- 可以用
super.属性名
来访问父类的属性,除了私有类的属性访问不到。 - 可以用
super.方法名(参数名)
来访问父类的方法,除了私有类的方法访问不到。 - 访问父类的构造器,只能放在构造器中的第一句(前面继承有讲)。
- super与this的比较:
方法重写/覆盖
- 方法重写就是子类有一个方法,和父类的某个方法的名称、返回类型、参数相同,那么我们就说子类的这个方法覆盖了父类的那个方法。
- 在主类中使用重写后的方法时,遵循就近原则。
- 子类方法的返回类型要和父类方法的返回类型相同,或者是父类返回类型的子类。
public Object getInfo() ----->返回类型为Object
public String getInfo()----->返回类型为String,是Object的子类
- 子类方法不能缩小父类方法的访问权限,但是可以扩大。
public > protected > 默认 > private
void sayOk() ----->父类
public sayOk() ------->子类
- 重写和重载的区别
public class OverrideExercise {
public static void main(String [] args) {
Person person = new Person("jack" , 10);
System.out.println(person.say());
Student student = new Student("tom",11,"1234",99);
System.out.println(student.say());
}
}
class Person {
private String name;
private int age;
public Person() {}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public String say() {
return "我是" + name + ",年龄是" + 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;
}
}
class Student extends Person {
private String id;
private int score;
public Student() {}
public Student(String name,int age,String id, int score) {
super(name,age);
this.id = id;
this.score = score;
}
public String say() { //这里体现super的好处
return super.say() + "学号:" + id + "分数:" + score;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
多态
方法的多态
方法或对象具有多种形态,多态是建立在继承和封装的基础之上的。
多态的前提:两个对象存在继承关系
public class Ploy01 {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//这里我们传入不同的参数,就会调用不同的sum方法,就体现多态。
System.out.println(a.sum(10,20)); //30
System.out.println(a.sum(10,20,30)); //60
//方法重写体现多态
B b = new B();
a.say(); //A say()方法被调用
b.say(); //B say()方法被调用
}
}
class B { ///父类
public void say() {
System.out.println("B say()方法被调用");
}
}
class A extends B { //子类
public int sum(int n1,int n2) {
return n1+n2;
}
public int sum(int n1,int n2,int n3) {
return n1+n2+n3;
}
public void say() {
System.out.println("A say()方法被调用");
}
}
对象的多态(核心、困难、重点)
一个对象的编译类型和运行类型可以不一致。
Animal animal = new Dog() 【编译类型是Animal(父类),运行类型是Dog(子类)】
- 编译类型在定义对象时,就确定了,不能改变。
- 运行类型是可以改变的。
- 编译类型看定义等号的左边,运行类型看定义等号的右边。
public class Plosy02 {
public static void main(String[] args) {
//对象多态的特点
Animal animal = new Dog();
animal.cry(); //Dog cry() 小狗在叫……
animal = new Cat();
animal.cry(); //Cat cry() 小猫在叫……
}
}
class Animal {
public void cry() {
System.out.println("Animal cry() 动物在叫……");
}
}
class Cat extends Animal {
public void cry() {
System.out.println("Cat cry() 小猫在叫……");
}
}
class Dog extends Animal {
public void cry() {
System.out.println("Dog cry() 小狗在叫……");
}
}
上转型
- 本质上是父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型()
Animal animal = new Dog()
- 可以调用父类中的所有成员(仍然需要遵守访问权限public等),不能调用子类特有成员。
- 运行效果看子类的具体实现,遵循就近原则。
下转型
- 语法:子类类型 引用名 = (子类类型) 父类引用 ------>括号代表强制转换
Cat cat = (Cat) animal
- 只能强转父类的引用,不能强转父类的对象。
- 要求父类的引用必须指向的是当前目标类型的对象。【即·下转型必须在上转型的基础上】
Animal anima = new Cat();
Cat cat = (Cat) animal;
Dog dog = (Dog) animal; //发生报错
- 下转型后可以调用子类类型中的所有成员。
属性重写
- 属性没有重写之说,属性的值看编译类型。
public class Ploy03 {
//属性没有重写之说,属性的值看编译类型
public static void main(String[] args) {
// TODO Auto-generated method stub
Base base = new Sub();
System.out.println(base.count); //10,与方法的就近原则不同,看编译类型
Sub sub = new Sub();
System.out.println(sub.count); //20
}
}
class Base {
int count = 10;
}
class Sub extends Base {
int count = 20;
}
instance of
比较操作符,用于判断对象的类型【运行类型】是否为某某类型的子类型或者某某类型。
public class Ploy04 {
public static void main(String[] args) {
// TODO Auto-generated method stub
BB bb = new BB();
//instance of 比较操作符,用于判断对象的类型【运行类型】是否为某某类型的子类型或者某某类型。
System.out.println(bb instanceof BB); //true
System.out.println(bb instanceof AA); //true
AA aa = new BB();
System.out.println(aa instanceof BB); //true
System.out.println(aa instanceof AA); //true
Object obj = new Object();
System.out.println(obj instanceof AA); //false
String str = "hello";
// System.out.println(str instanceof AA); 报错
System.out.println(str instanceof Object); //true
}
}
class AA {
}
class BB extends AA {
}
练习
java动态绑定机制(*******)
- 当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定。
- 当调用属性时,没有动态绑定机制,哪里声明,哪里使用。
public class Ploy05 {
public static void main(String[] args) {
// TODO Auto-generated method stub
E e = new F();
//动态绑定机制
// 子类中没有sum方法,则调用父类的sum方法。
//运行父类中sum方法中【运行类型子类的getI()方法】
//当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定。
System.out.println(e.sum()); //30 = 20 + 10
//当调用属性时,没有动态绑定机制,哪里声明,哪里使用。
System.out.println(e.sum1()); // 10 + 10 = 20
}
}
class E {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i+10;
}
public int getI() {
return i;
}
}
class F extends E{
public int i = 20;
// public int sum() {
// return i+20;
// }
public int getI() {
return i;
}
// public int sum1() {
// return i+10;
// }
}
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
public class T {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person [] persons = new Person[5];
persons[0] = new Person("jack",20);
persons[1] = new Student("tom",21,99);
persons[2] = new Student("xx",19,60);
persons[3] = new Teacher("yy",35,1000);
persons[4] = new Teacher("king",40,2000);
//循环遍历多态数组,调用say方法
for(int i=0; i<5; i++) {
persons[i].say(); //动态绑定机制
if(persons[i] instanceof Student) { //instanceof是判断运行类型
Student student = (Student)persons[i]; //下转型
student.study();
//也可以使用下面一条语句
((Student)persons[i]).study();
} else if(persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i]; //下转型
teacher.teach();
} else {
System.out.println("类型有误");
}
}
}
}
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型。
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Worker worker = new Worker("jack",2000);
Manager manager = new Manager("tom",1000,2000);
Test test = new Test();
test.showEmpAnnual(worker);
test.testWork(worker);
}
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
//添加一个方法,如果是员工则使用work方法,如果是经理则使用manage方法。
public void testWork(Employee e) {
//类似于多态数组
if(e instanceof Worker) {
Worker worker = (Worker) e; //下转型
((Worker) e).work();
} else if(e instanceof Manager) {
Manager manager = (Manager) e;
((Manager) e).manage();
} else {
System.out.println("不做处理");
}
}
}
Object类详解
属于lang包中的顶级类
equals
==
【比较运算符】和equals
的区别:
==
既可以判断基本类型,又可以判断引用类型。==
如果判断基本类型,判断的值是否相等。==
如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象。
public class Equals01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
A b = a;
A c = b;
System.out.println(a == c); //true
B obj = a;
System.out.println( obj == c); //true
}
}
class B {
}
class A extends B {
}
equals
:Object
类中的方法,只能判断引用类型。
Objec类中的equals方法是判断两个对象是否是用一个对象即地址是否相等。
"hello".equals ("abc")