java(面向对象 )
java面向对象的语言
对象:真实存在唯一的事物。
类:实际就是对某种类型事物的共性属性与行为的抽取。
面向对象的计算机语言核心思想: 找适合的对象做适合的事情。
如何找适合的对象:
1.sun已经定义好了很多的类,我们只需要认识这些类,我们就可以通过这些类创建对象使用。
2.自定义类,通过自定义类来创建对象使用。
自定义类创建对象的三步骤:
- 自定义类。
格式:
class 类名{
事物的公共属性使用成员变量描述。
事物的公共行为使用函数描述。
}
-
通过自定义类创建对象。
格式:类名 变量名 = new 类名();
3.访问(设置)对象的属性或者调用对象的功能。
1.访问对象属性的格式:
对象.属性名.
2.设置对象的属性:
对象.属性名 = 数据。
3.调用对象的功能
对象.函数名();
成员变量与局部变量的区别:
定义的位置上区别:
1.成员变量是定义在方法之外,类之内的。
2.局部变量是定义在方法之内。
作用上的区别:
1.成员变量的作用是用于描述一类事物的公共 属性的。
2.局部变量的作用就是提供一个变量给方法内部使用而已。
生命周期区别:
1.随着对象 的创建而存在,随着对象的消失而消失。
2.局部变量在调用了对应的方法时执行到了创建该变量的语句时存在,局部变量一旦出了自己的作用域那么马上从内存中消失。
初始值的区别:
1.成员变量是有默认的初始值。
数据类型 默认的初始值
int 0
float 0.0f
double 0.0
boolean false
char ' '
String(引用数据类型) null
2.局部变量是没有默认的初始值的,必须要先初始化才能使用。
//人类
class Person {
String name; //成员变量
public void eat(){
int age ;//局部变量
age = 12;
for(int i = 0 ; i< 5 ; i++){
System.out.println("hello world"+ age);
}
}
}
class Demo3 {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
}
}
匿名对象:没有引用类型变量指向的对象称作为匿名对象。
匿名对象要注意的事项:
1.我们一般不会给匿名对象赋予属性值,因为永远无法获取到。
2.两个匿名对象永远都不可能是同一个对象。
匿名对象好处:简化书写。
匿名对象的应用场景:
1.如果一个对象需要调用一个方法一次的时候,而调用完这个方法之后,该对象就不再使用了,这时候可以使用匿名对象。
2.可以作为实参调用一个函数。
//学生类
class Student {
int num; //学号
String name; //名字
public void study() {
System.out.println("hello world!");
}
}
class Demo4 {
public static void main(String[] args) {
//创建一个学生对象
//Student s = new Student();
//new Student().name = "xxx"; //匿名对象
//System.out.println(Student().name); //null
System.out.println(new Student() == new Student()) ; // "==" 用于引用类型变量时,比较的是内存地址。判断两个对象是否为同一个对象
//需求:调用Student的study方法。
Student s = new Student();
s.study();
new Student().study();
}
}
面向对象三大特征:
1.封装
2.继承
3.多态
封装:
权限修饰符:权限修饰符就是控制变量可见范围的。
public:公共的。public修饰的成员变量或者方法任何人都可以直接访问。
private:私有的,private修饰的成员变量或者方法只能在本类中进行直接访问。
封装的步骤:
1.使用private修饰需要被封装的属性。
2.提供一个公共的方法设置或者获取该私有的成员属性。
命名规范:
set属性名();
get属性名();
规范:在现实开发中一般实体类的所有成员属性(成员变量)都要封装起来。
实体类:实体类就是用于描述一类事物的就称作为实体类。
封装的好处:
1.提高数据的安全性。
2.操作简单。
3.隐藏了实现。
class Member{
public String name; //名字
private String sex; //性别
public int salary; //薪水
//定义一个公共的方法设置sex属性
public void setSex(String s){
if (s.equals("男")||s.equals("女")){ //注意: 如果比较两个字符串的内容是否一致,不要使用==比较, 使用equals方法。
sex = s;
}else{
//默认是男
sex = "男";
}
}
//定义一个公共的方法获取sex属性
public String getSex() {
return sex;
}
//聊天
public void talk() {
System.out.println(name+"聊得非常开心");
}
}
class Demo5 {
public static void main(String[] args) {
Member m = new Member();
m.name="狗娃";
m.setSex("女");
m.salary = 800;
System.out.println("姓名:"+ m.name+" 性别:"+ m.getSex()+" 薪水:"+ m.salary);
}
}
eg:使用java类描述一个计算器类,计算器具备操作数1,操作数2、操作符三个公共的属性,还具备计算的功能行为。
计算器类:
class Calculator{
private int num1; //操作数1
private int num2; //操作数2
private char option ; //运算符
//提供公共的方法设置属性值....
public void initCalculator(int n1 , int n2 , char o){
num1 = n1;
num2 = n2;
if(o=='+'||o=='-'||o=='*'||o=='/'){
option = o;
}else{
option = '+';
}
}
//计算的功能
public void calculate(){
switch(option){
case '+':
System.out.println("做加法运算,结果是:"+(num1+num2));
break;
case '-':
System.out.println("做减法运算,结果是:"+(num1-num2));
break;
case '*':
System.out.println("做乘法运算,结果是:"+(num1*num2));
break;
case '/':
System.out.println("做除法运算,结果是:"+(num1/num2));
break;
}
}
}
主类:
class Demo6 {
public static void main(String[] args) {
//创建了一个计算器对象
Calculator c = new Calculator();
//设置属性值
c.initCalculator(1,2,'a');
//调用计算器的计算功能
c.calculate();
}
}
构造函数:
构造函数的作用: 给对应的对象进行初始化。
构造函数的定义的格式:
修饰符 函数名(形式参数){
函数体...
}
构造函数要注意的细节:
1.构造函数是没有返回值类型的。
2.构造函数的函数名必须要与类名一致。
3.构造函数并不是由我们手动调用的,而是在创建对应的对象时,jvm就会主动调用到对应的构造函数。
4.如果一个类没有显式的写上一个构造方法时,那么java编译器会为该类添加一个无参的构造函数的。
5.如果一个类已经显式的写上一个构造方法时,那么java编译器则不会再为该类添加 一个无参 的构造方法。
6.构造函数是可以在一个类中以函数重载的形式存在多个 的。
创建对象时,jvm就会调用到对应的构造方法,那么我们以前没有学构造方法,那么以前创建对象时,jvm是否 也会调用构造方法呢?如果有?构造方法从何而来呢?
会调用, java编译器在编译的 时候给加上去的。
jdk提供了一个java开发工具(javap.exe)给我们进行反编译的。
javap 反编译工具的使用格式:
javap -c -l -private 类名 //可以进行查看
java编译器添加 的无参构造方法的权限修饰符是 什么?
与类的权限修饰是一致的。
构造函数与普通函数的区别:
1.返回值类型的区别:
1.构造函数是没有返回值类型的,
2.普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。
2.函数名的区别:
1.构造函数的函数名必须要与类名一致,
2.普通函数的函数名只要符合标识符的命名规则即可。
3.调用方式的区别:
1.构造函数是在创建对象的时候由jvm调用的。
2.普通函数是由我们使用对象调用的,一个对象可以对象多次普通的函数。
4.作用上的区别:
1.构造函数的作用用于初始化一个对象。
2.普通函数是用于描述一类事物的公共行为的。
class Baby{
int id; //身份证
String name; //名字
//构造函数
public Baby(int i , String n){
id = i;
name = n;
System.out.println("baby的属性初始化完毕!!");
}
//无参的构造函数
public Baby(){
System.out.println("无参的构造函数被调用了..");
}
//哭
public void cry(){
System.out.println(name+"哇哇哭...");
}
}
class Demo2 {
public static void main(String[] args) {
//创建一个baby对象
Baby b1 = new Baby(110,"狗娃"); //婴儿诞生 白户
System.out.println("编号:"+ b1.id +" 姓名:"+ b1.name);
b1.cry();
b1.cry();
//黑户
Baby b2 = new Baby();
new Baby();
b2.id = 112;
b2.name = "狗剩";
System.out.println("编号:"+ b2.id +" 姓名:"+ b2.name);
}
}
构造代码块:
构造代码块的作用:给对象进行统一的初始化。
构造函数的作用:给对应的对象进行初始化。
构造代码块的格式:
{
构造代码块
}
注意:构造代码块的大括号必须位于成员位置上。
代码块的类别:
1.构造代码块。
2.局部代码块。大括号位于方法之内。作用:缩短局部变量的生命周期,节省一点点内存。
3.静态代码块 static
class Baby{
int id; //身份证
String name; //名字
//构造代码块...
{
//System.out.println("构造代码块的代码执行了......");
}
//带参构造函数
public Baby(int i , String n){
id = i;
name = n;
}
//无参构造方法
public Baby(){
}
public void cry(){
System.out.println(name+"哇哇哭...");
}
}
class Demo4 {
public static void main(String[] args) {
Baby b1 = new Baby(110,"狗娃"); // 狗娃 狗剩 铁蛋
System.out.println("编号:"+ b1.id + " 名字:"+b1.name);
System.out.println("编号:"+ b1.id + " 名字:"+b1.name);
new Baby(112,"狗剩");
new Baby();
new Baby(110,"狗娃");
new Baby(112,"狗剩");
new Baby();
}
}
构造代码块要注意的事项:
1.java编译器编译一个java源文件的时候,会把成员变量的声明语句提前至一个类的最前端。
2.成员变量的初始化工作其实都在在构造函数中执行的。
3.一旦经过java编译器编译后,那么构造代码块的代码块就会被移动构造函数中执行,是在构造函数之前执行的,构造函数的中代码是最后执行的。
4.成员变量的显示初始化与构造代码块的代码是按照当前代码的顺序执行的。
class Demo5 {
//构造函数
public Demo5() { //构造函数
i = 300000000;
}
//构造代码块 //构造代码块的初始化
{
i = 200000000;
}
int i = 100000000; //成员变量的显初始化
public static void main(String[] args) {
Demo5 d = new Demo5();
System.out.println("i = "+d.i); // 双、吴、刘 :200000000 :
}
}
this关键字:
this关键字代表了所属函数的调用者对象。
this关键字作用:
1.如果存在同名成员变量与局部变量时,在方法内部默认是访问局部变量的数据,可以通过this关键字指定访问成员变量的数据。
2.在一个构造函数中可以调用另外一个构造函数初始化对象。
this关键字调用其他的构造函数要注意的事项:
1.this关键字调用其他的构造函数时,this关键字必须要位于构造函数中的第一个语句。
2.this关键字在构造函数中不能出现相互调用的情况,因为是一个死循环。
this关键字要注意事项:
1.存在同名的成员变量与局部变量时,在方法的内部访问的是局部变量(java 采取的是“就近原则”的机制访问的。)
2.如果在一个方法中访问了一个变量,该变量只存在成员变量的情况下,那么java编译器会在该变量的前面添加this关键字。
class Animal{
String name ; //成员变量
String color;
public Animal(String n , String c){
name = n;
color = c;
}
//this关键字代表了所属函数的调用者对象
public void eat(){
//System.out.println("this:"+ this);
String name = "老鼠"; //局部变量
System.out.println(name+"在吃..."); //需求: 就要目前的name是成员变量的name.
}
}
class Demo6 {
public static void main(String[] args) {
Animal dog = new Animal("狗","白色"); //现在在内存中存在两份name数据。
Animal cat = new Animal("猫","黑色");
cat.eat();
}
}
this关键字调用其他的构造函数要注意的事项:
1.this关键字调用其他的构造函数时,this关键字必须要位于构造函数中的第一个语句。
2.this关键字在构造函数中不能出现相互调用 的情况,因为是一个死循环。
class Student {
int id; //身份证
String name; //名字
//目前情况:存在同名 的成员 变量与局部变量,在方法内部默认是使用局部变量的。
public Student(int id,String name) { //一个函数的形式参数也是属于局部变量。
this(name); //调用了本类的一个参数的构造方法
//this(); //调用了本类无参的 构造方法。
this.id = id; // this.id = id 局部变量的id给成员变量的id赋值
System.out.println("两个参数的构造方法被调用了...");
}
public Student() {
System.out.println("无参的构造方法被调用了...");
}
public Student(String name){
this.name = name;
System.out.println("一个参数的构造方法被调用了...");
}
}
class Demo7 {
public static void main(String[] args) {
Student s = new Student(110,"铁蛋");
System.out.println("编号:"+ s.id +" 名字:" + s.name);
Student s2 = new Student("金胖子");
System.out.println("名字:" + s2.name);
}
}
static(静态\修饰符)关键字
1.static修饰成员变量:如果有数据需要被共享给所有对象使用时,那么就可以使用static修饰。
静态成员变量的访问方式:
方式1: 可以使用对象进行访问。
格式: 对象.变量名。
方式二: 可以使用类名进行访问。
格式: 类名.变量名;
注意:
1.非静态的成员变量只能使用对象进行访问,不能使用类名进行访问。
2.千万不要为了方便访问数据而使用static修饰成员变量,只有成员变量的数据是真正需要被共享的时候才使用static修饰。
static修饰成员变量的应用场景:如果一个数据需要被所有对象共享使用的时候,这时候即可好实用static修饰。
class Emp{
static int count = 0; //计数器
String name;
//构造代码块
{
count++;
}
public Emp(String name){
this.name = name;
}
public Emp(){ //每创建一个对象的时候都会执行这里 的代码
}
public void showCount(){
System.out.println("创建了"+ count+"个对象");
}
}
class Demo11 {
public static void main(String[] args) {
Emp e1 = new Emp();
Emp e2 = new Emp();
Emp e3 =new Emp();
e3.showCount();
}
}
public class Calculator{
private int num1; //操作数1
private int num2; //操作数2
private char option ; //运算符
//提供公共的方法设置属性值....
public void initCalculator(int n1 , int n2 , char o){
num1 = n1;
num2 = n2;
if(o=='+'||o=='-'||o=='*'||o=='/'){
option = o;
}else{
option = '+';
}
}
//计算的功能
public void calculate(){
switch(option){
case '+':
System.out.println("做加法运算,结果是:"+(num1+num2));
break;
case '-':
System.out.println("做减法运算,结果是:"+(num1-num2));
break;
case '*':
System.out.println("做乘法运算,结果是:"+(num1*num2));
break;
case '/':
System.out.println("做除法运算,结果是:"+(num1/num2));
break;
}
}
}
静态的成员变量与非静态的成员变量的区别:
1.作用上的区别:
1.静态的成员变量的作用共享一个数据给所有的对象使用。
2.非静态的成员变量的作用是描述一类事物的公共属性。
2.数量与存储位置上的区别:
1.静态成员变量是存储方法区内存中,而且只会存在一份数据。
2.非静态的成员变量是存储在堆内存中,有n个对象就有n份数据。
3.生命周期的区别:
1.静态的成员变量数据是随着类的加载而存在,随着类文件的消失而消失。
2.非静态的成员数据是随着对象的创建而存在,随着对象被垃圾回收器回收而消失。
静态函数:
静态函数要注意的事项:
1.静态函数是可以调用类名或者对象进行调用的,而非静态函数只能使用对象进行调用。
2.静态的函数可以直接访问静态的成员,但是不能直接访问非静态的成员。
原因:静态函数是可以使用类名直接调用的,这时候可能还没有存在对象,而非静态的 成员数据是随着对象的存在而存在的。
3.非静态的函数是可以直接访问静态与非静态的成员。
原因:非静态函数只能由对象调用,当对象存在的时候,静态数据老早就已经存在了,而非静态数据也随着对象的创建而存在了。
4.静态函数不能出现this或者super关键字。
原因:因为静态的函数是可以使用类名调用的,一旦使用类名调用这时候不存在对象,而this关键字是代表了一个函数的调用者对象,这时候产生了冲突。
静态的数据的生命周期:静态的成员变量数据是优先于对象存在的。
static什么时候修饰一个函数?
如果一个函数没有直接访问到非静态的成员时,那么就可以使用static修饰了。 一般用于工具类型的方法。
静态函数不能访问非静态的成员?
静态函数只要存在有对象,那么也可以访问非 静态的数据。只是不能直接访问而已。
class Student{
String name; //名字
static String country = "中国"; //国籍
//静态代码块 :静态代码块是在Student.class文件加载到内存的时候就马上执行的。
static {
System.out.println("静态代码块执行了...");
}
//构造函数
public Student(String name) {
this.name = name;
}
//非静态的成员函数
public void study() {
System.out.println("好好学习"+this);
}
//静态函数
public static void sleep() { //静态方法与非静态方法的字节码文件是同时存在内存中的。只是静态的成员变量数据是优先于对象存在而已。
Student s= new Student("铁蛋");
System.out.println(s.name+"呼呼大睡...");
}
}
class Demo2 {
public static void main(String[] args) {
Student.sleep();
// Student s = new Student("狗娃");
}
}
main函数的详解:
public :公共的。权限是最大,在任何情况下都可以访问。private
原因:为了保证让jvm在任何情况下都可以访问到main方法。
static: 静态。静态可以让jvm调用main函数的时候更加的方便。不需要通过对象调用。
void: 没有返回值。因为返回的数据是给jvm,而jvm使用这个数据是没有意义的。所以就不要了。
main: 函数名。注意:main并不是关键字,只不过是jvm能识别的一个特殊的函数名而已。
arguments:担心某些程序在启动需要参数。
class Demo4 {
public static void main(String[] args) {
System.out.println("数组的长度:"+ args.length);
for(int i = 0 ; i <args.length ; i++){
System.out.print(args[i]+",");
}
Scanner scanner = new Scanner(System.in);
}
}
继承:继承是通过关键字extends体现的。
继承的格式:
class 类名1 extends 类名2{
}
继承要注意的事项:
1.千万不要为了减少重复代码而去继承,只有真正存在着继承关系的时候才去继承。
2.父类私有的成员不能被继承。
3.父类的构造函数不能被继承。
4.创建子类对象时默认会先调用父类无参的构造函数。
class Person{
String name;
private int age;
public Person(String name){
this.name = name;
}
public Person(){//无参的构造方法
System.out.println("Person类的构造方法被调用了....");
}
public void eat(){
System.out.println(name+"在吃饭...");
}
}
//学生类
class Student extends Person { // Student 就称作为Person类的子类, Person类就称作为Student的父类(超类、基类)
int num; //学号
public Student(){
System.out.println("Student类的构造方法被调用了....");
}
public void study(){
System.out.println(name+"good good study , day day up");
}
}
class Demo7 {
public static void main(String[] args) {
Student s = new Student();
/*
s.name = "狗娃";
System.out.println("名字:"+ s.name);
s.eat();
*/
}
}
疑问:为什么要调用父类的构造方法啊?这样子做的意义在那?
调用父类的构造方法是可以初始化从父类继承下去的属性的。
class Fu{
int x = 10;
String name;
public Fu(String name){
this.name = name;
System.out.println("Fu类d带参的构造方法...");
}
public Fu(){
System.out.println("Fu类无参的构造方法...");
}
}
class Zi extends Fu{
int x = 20;
public Zi(String name){
super(name); //指定调用父类一个参数的构造函数。
}
public void print(){
System.out.println("x1 = "+ x);
}
}
class Demo8 {
public static void main(String[] args) {
Zi z = new Zi("大头儿子");
System.out.println("name= "+z.name);
}
}
super关键字:
super关键字代表了父类空间的引用。
super关键字的作用:
1.子父类存在着同名的成员时,在子类中默认是访问子类的成员,可以通过super关键字指定访问父类的成员。
2.创建子类对象时,默认会先调用父类无参的构造方法,可以通过super关键字指定调用父类的构造方法。
super关键字调用父类构造方法要注意的事项:
1.如果在子类的构造方法上没有指定调用父类的构造方法,那么java编译器会在子类的构造方法上面加上super()语句。
2.super关键字调用父类的构造函数时,该语句必须要是子类构造函数中的第一个语句。
3.super与this关键字不能同时出现在同一个构造函数中调用其他的构造函数。因为两个语句都需要第一个语句。
super关键字与this关键字的区别:
- 代表的事物不一致。
1.super关键字代表的是父类空间的引用。
2.this关键字代表的是所属函数的调用者对象。
- 使用前提不一致。
1.super关键字必须要有继承关系才能使用。
2.this关键字不需要存在继承关系也可使用。
- 调用构造函数的区别:
1.super关键字是调用父类的构造函数。
2.this关键字是调用本类的构造函数。
class Fu{
int x = 10;
String name;
public Fu(){
System.out.println("Fu类无参的构造方法..");
}
public Fu(String name){
this.name = name;
System.out.println("Fu类带参的构造方法..");
}
public void eat(){
System.out.println("小头爸爸吃番薯..");
}
}
class Zi extends Fu{
int x = 20;
int num;
public Zi(String name,int num){
super(name); //指定调用了父类带参的构造方法...
this(); // 调用本类无参构造方法..
//super(); //指定调用了父类无参构造方法。。。
System.out.println("Zi类带参的构造方法..");
}
public Zi(){
System.out.println("Zi类无参的构造方法..");
}
public void print(){
System.out.println("x = " +super.x);
}
public void eat(){
System.out.println("大头儿子吃龙虾..");
}
}
class Demo9 {
public static void main(String[] args) {
Zi z = new Zi("狗娃");
}
}
方法重写
方法重写的前提:必须要存在继承的关系。
方法的重写:子父类出了同名的函数,这个我们就称作为方法的重写。
什么是时候要使用方法的重写:父类的功能无法满足子类的需求时。
方法重写要注意的事项:
1.方法重写时,方法名与形参列表必须一致。
2.方法重写时,子类的权限修饰符必须要大于或者等于父类的权限修饰符。
3.方法重写时,子类的返回值类型必须要小于或者 等于父类的返回值类型。
4.方法重写时,子类抛出的异常类型要小于或者等于父类抛出的异常类型。
Exception(最坏)
RuntimeException(小坏)
class Student{
String name;
//构造函数
public Student(String name){
this.name = name;
}
public void study(){
System.out.println(name+"学习马克思列宁主义");
}
}
//基础班的学生是属于学生中一种
class BaseStudent extends Student{
public BaseStudent(String name){
super(name);//指定调用父类构造函数
}
//重写
public void study(){
System.out.println(name+"学习javase..");
}
}
//就业班学生也是属于普通学生中一种
class WorkStudent extends Student{
//构造函数
public WorkStudent(String name){
super(name);//指定调用父类构造函数
}
//重写
public void study(){
System.out.println(name+"学习javaee+android..");
}
}
class Demo11 {
public static void main(String[] args) {
//System.out.println("Hello World!");
BaseStudent s = new BaseStudent("居东东");
s.study();
//创建一个就业班的学生
WorkStudent w = new WorkStudent("张三");
w.study();
}
}
方法的重载
方法的重载:在一个类中存在两个或者两个以上的同名函数,称作为方法重载。
方法重载的要求
1.函数名要一致。
2.形参列表不一致(形参的个数或形参的类型不一致)
3.与返回值类型无关。
instanceof 关键字
instanceof关键字的作用:判断一个对象是否属于指定的类别。
instanceof关键字的使用前提:判断的对象与指定的类别必须要存在继承或者实现的关系。
instanceof关键字的使用格式:
对象 instanceof 类别
instanceof关键字的作用:目前没用。但是后天我们学习到了多态之后就非常有用。一般我们做强制类型转换之前都会使用该关键字先判断一把,然后在进行转换的。
class Animal{
String name;
String color;
public Animal(String name, String color){
this.name = name;
this.color = color;
}
}
//狗是属于动物中一种
class Dog extends Animal {
public Dog(String name,String color){
super(name,color); //指定调用父类两个 参数的构造函数。
}
public void bite(){
System.out.println(name+"咬人!!");
}
}
//老鼠 也是属于动物中一种
class Mouse extends Animal{
public Mouse(String name,String color){
super(name,color);
}
public void dig(){
System.out.println(name+"打洞..");
}
}
class Demo12 {
public static void main(String[] args) {
Dog d = new Dog("哈士奇","白色");
System.out.println("狗是狗类吗?"+ (d instanceof Dog));
System.out.println("狗是动物类吗?"+ (d instanceof Animal));
//System.out.println("狗是老鼠类吗?"+ (d instanceof Mouse));//false
Animal a = new Animal("狗娃","黄色"); //狗娃是人
System.out.println("动物都是狗吗?"+ (a instanceof Dog));
}
}
final(最终、修饰符)
final关键字的用法:
1.final关键字修饰一个基本类型的变量时,该变量不能重新赋值,第一次的值为最终的。
2.fianl关键字修饰一个引用类型变量时,该变量不能重新指向新的对象。
3.final关键字修饰一个函数的时候,该函数不能被重写。
4.final关键字修饰一个类的时候,该类不能被继承。
常量的修饰符一般为: public static final
class Circle {
double r; //半径
public static final double pi = 3.14; //固定不变的
public Circle(double r) {
this.r = r;
}
//计算面积
public final void getArea() {
System.out.println("圆形的面积是:"+r*r*pi);
}
}
class Demo2 extends Circle {
public Demo2(double r) {
super(r);
}
public static void main(String[] args) {
/*
final Circle c = new Circle(4.0);
test(c);
*/
Demo2 c = new Demo2(4.0);
c.getArea();
}
public static void test(Circle c) {
c = new Circle(5.0); //c变量又重新指向了新的对象。
c.getArea();
}
}
抽象类:abstract
目前存在的问题:
1.动物类的run方法描述的不正确。
2.没有强制要子类一定要重写run方法。
抽象类的应用场景:
我们在描述一类事物的时候,发现该种事物确实存在着某种行为,但是这种行为目前是不具体的,那么我们可以抽取这种行为的声明,但是不去实现该种行为,这时候这种行为我们称作为抽象的行为,我们就需要使用抽象类。
抽象类的好处:强制要求子类一定要实现指定的方法。
抽象类要注意的细节:
1.如果一个函数没有方法体,那么该函数必须要使用abstract修饰,把该函数修饰成抽象的函数。
2.如果一个类出现了抽象的函数,那么该类也必须 使用abstract修饰。
3.如果一个非抽象类继承了抽象类,那么必须要把抽象类的所有抽象方法全部实现。
4.抽象类可以存在非抽象方法,也可以存在抽象的方法.
5.抽象类可以不存在抽象方法的。
5.抽象类是不能创建对象的。
疑问:为什么抽象类不能创建对象呢?
因为抽象类是存在抽象方法的,如果能让抽象类创建对象的话,那么使用抽象的对象调用抽象方法是没有任何意义的。
6.抽象类是存在构造函数的,其构造函数是提供给子类创建对象的时候初始化父类的属性的。
//动物类--->抽象类
abstract class Animal{
String name;
String color;
public Animal(String name,String color){
this.name = name;
this.color = color;
}
//非抽象的方法
public void eat(){
System.out.println(name+"吃粮食");
}
//移动...
public abstract void run();
}
//狗 是属于动物中一种
class Dog extends Animal{
public Dog(String name,String color){
super(name,color);
}
public void run() {//重写父类抽象的方法
System.out.println(name+"四条腿跑得很快...");
}
}
//鱼 是属于动物中一种
class Fish extends Animal{
public Fish(String name,String color){
super(name,color);
}
public void run(){//重写父类抽象的方法
System.out.println(name+"摇摇尾巴游啊游!");
}
}
class Demo3 {
public static void main(String[] args) {
/*
Dog d = new Dog("牧羊犬","棕色");
d.run();
//创建一个鱼对象
Fish f = new Fish("锦鲤","金黄色");
f.run();
*/
Animal a = new Animal();
}
}
abstract不能与以下关键字共同修饰一个方法:
1.abstract不能与private共同修饰一个方法。
2.abstract不能与static共同修饰一个方法。
3.abstract不能与final共同修饰一个方法。
//图形类
abstract class MyShape{
String name;
public MyShape(String name){
this.name = name;
}
public abstract void getArea();
public abstract void getLength();
}
//圆形是属于图形类的一种
class Circle extends MyShape{
double r;
public static final double PI = 3.14;
public Circle(String name,double r){
super(name);
this.r =r;
}
public void getArea(){
System.out.println(name+"的面积是:"+PI*r*r);
}
public void getLength(){
System.out.println(name+"的周长是:"+2*PI*r);
}
}
//矩形 属于图形中的 一种
class Rect extends MyShape{
int width;
int height;
public Rect(String name,int width, int height){
super(name);
this.width = width;
this.height = height;
}
public void getArea(){
System.out.println(name+"的面积是:"+width*height);
}
public void getLength(){
System.out.println(name+"的周长是:"+2*(width+height));
}
}
class Demo4 {
public static void main(String[] args) {
//System.out.println("Hello World!");
Circle c = new Circle("圆形",4.0);
c.getArea();
c.getLength();
//矩形
Rect r = new Rect("矩形",3,4);
r.getArea();
r.getLength();
}
}
接口:interface
接口的定义格式:
interface 接口名{
}
接口要注意的事项:
1.接口是一个特殊的类。
2.接口的成员变量默认的修饰符为:public,static,final。那么也就是说接口中的成员变量都是常量。
3.接口中的方法都是抽象的方法,默认的修饰符为:public,abstract。
4.接口不能创建对象。
5.接口是没有构造方法的。
6.接口是给类去实现使用的,非抽象类实现一个接口的时候,必须要把接口中所有方法全部实现。
实现接口的格式:implements
class 类名 implements 接口名{
}
interface A {
//成员变量
public static final int i=10;
//成员函数
public void print();
}
class Demo7 implements A { // Demo7就实现了A接口
public static void main(String[] args) {
Demo7 d = new Demo7();
d.print();
}
//实现接口中的方法
public void print(){
System.out.println("这个是接口中的print方法...");
}
}
接口的作用:
1.程序的解耦。(低耦合)
2.定义约束规范。
3.拓展功能。
//普通的铅笔类
class Pencil{
String name;
public Pencil(String name){
this.name = name;
}
public void write(){
System.out.println(name+"沙沙的写...");
}
}
//橡皮接口
interface Eraser{
public void remove();
}
//带橡皮的铅笔
class PencilWithEraser extends Pencil implements Eraser {
public PencilWithEraser(String name){
super(name);
}
public void remove(){
System.out.println("涂改,涂改....");
}
}
class Demo8 {
public static void main(String[] args) {
//System.out.println("Hello World!");
PencilWithEraser p = new PencilWithEraser("2B铅笔");
p.write();
p.remove();
}
}
类与接口之间关系:实现关系。
类与接口要注意的事项:
1.非抽象类实现一个接口时,必须要把接口中所有方法全部实现。
2.抽象类实现一个接口时,可以实现也可以不实现接口中的方法。
3.一个类可以实现多个接口。
疑问:java为什么不支持多继承,而支持了多实现呢?
class A{
public void print(){
System.out.println("AAAAAA");
}
}
class B{
public void print(){
System.out.println("BBBBBB");
}
}
class C extends A ,B{
}
new C().print();
接口与接口之间关系:继承关系。
接口与接口之间要注意事项:
一个接口是可以继承多个接口的。
多态:
一个对象具备多种形态。(父类的引用类型变量指向了子类的对象)或者是接口的引用类型变量指向了接口实现类的对象)
多态的前提:必须存在继承或者实现关系。
动物 a = new 狗();
多态要注意的细节:
1.多态情况下,子父类存在同名的成员变量时,访问的是父类的成员变量。
2.多态情况下,子父类存在同名的非静态的成员函数时,访问的是子类的成员函数。
3.多态情况下,子父类存在同名的静态的成员函数时,访问的是父类的成员函数。
4.多态情况下,不能访问子类特有的成员。
总结:多态情况下,子父类存在同名的成员时,访问的都是父类的成员,除了在同名非静态函数时才是访问子类的。
编译看左边,运行不一定看右边。
编译看左边:java编译器在编译的时候,会检查引用类型变量所属的类是否具备指定的成员,如果不具备马上编译报错。
//动物类
abstract class Animal {
String name;
String color = "动物色";
public Animal(String name){
this.name = name;
}
public abstract void run();
public static void eat(){
System.out.println("动物在吃..");
}
}
//老鼠
class Mouse extends Animal{
String color = "黑色";
public Mouse(String name){
super(name);
}
public void run(){
System.out.println(name+"四条腿慢慢的走!");
}
public static void eat(){
System.out.println("老鼠在偷吃..");
}
//老鼠特有方法---打洞
public void dig(){
System.out.println("老鼠在打洞..");
}
}
class Fish extends Animal {
public Fish(String name){
super(name);
}
public void run(){
System.out.println(name+"摇摇尾巴游..");
}
}
class Demo11 {
public static void main(String[] args) {
/*
Mouse m = new Mouse("老鼠");
System.out.println(m.color);
//多态: 父类的引用类型变量指向子类的对象
*/
Animal a = new Mouse("老鼠");
a.dig();
//a.eat();
}
}
多态的应用:
1.多态用于形参类型的时候,可以接收更多类型的数据 。
2.多态用于返回值类型的时候,可以返回更多类型的数据。
多态的好处:提高了代码的拓展性。
//图形类
abstract class MyShape {
public abstract void getArea();
public abstract void getLength();
}
class Circle extends MyShape {
public static final double PI = 3.14;
double r;
public Circle(double r){
this.r =r ;
}
public void getArea() {
System.out.println("圆形的面积:"+ PI*r*r);
}
public void getLength() {
System.out.println("圆形的周长:"+ 2*PI*r);
}
}
class Rect extends MyShape {
int width;
int height;
public Rect(int width , int height) {
this.width = width;
this.height = height;
}
public void getArea(){
System.out.println("矩形的面积:"+ width*height);
}
public void getLength(){
System.out.println("矩形的周长:"+ 2*(width+height));
}
}
class Demo12 {
public static void main(String[] args) {
/*
//System.out.println("Hello World!");
Circle c = new Circle(4.0);
print(c);
Rect r = new Rect(3,4);
print(r);
*/
MyShape m = getShape(0); //调用了使用多态的方法,定义的变量类型要与返回值类型一致。
m.getArea();
m.getLength();
}
//需求1:定义一个函数可以接收任意类型的图形对象,并且打印图形面积与周长。
public static void print(MyShape s) { //MyShpe s = new Circle(4.0);
s.getArea();
s.getLength();
}
//需求2:定义一个函数可以返回任意类型的图形对象。
public static MyShape getShape(int i) {
if (i==0){
return new Circle(4.0);
}else{
return new Rect(3,4);
}
}
}