继承的详细介绍

1 继承的概念
    概念:
        我们说类是客观对象在人脑中的一种反应,是人对对象的一种认识,
        
        在我们的头脑中有狗类和猫类的认识
        但是在我们人脑中还有动物类,那你知道什么是动物类,这个类从哪里来的,你的人脑中为什么有动物类的认识
        狗类,猫类,是我们从狗对象和猫对象中分析出共性抽取出来形成了对一类事物的认识,那动物类是怎么来的 
        是因为你见过一个动物对象吗,你有没有见过这样一个对象,不是狗,不是猫,不是狮子,不是老虎,不是大象,
        就是一个动物,没有吧。那么问题就来了既然你没有见过这样一个对象那这个类是哪里来的,
        进而分析那你知不知道动物这个类和狗类猫类的关系呢,
        狗是一种特殊的动物,猫也是一种特殊的动物 其他的呢也都是动物,
        所以说动物这个类的由来:是在狗类,猫类等抽象出来的。我们就把这种关系叫做继承关系  
         
        父类--->动物
        子类--->猫,狗
            |  猫类
        动物|
            |  狗类
            
        我们从对象抽象出类  又从类中抽象出父类
        总之 父类---子类  是从一般到特殊   
        父类:子类的共性抽象出来而形成的或者说子类是特殊的父类
            猫: 会吃  会睡  会叫 会跑  会捉老鼠
            狗: 会吃  会睡  会叫 会跑
            鱼: 会吃  会睡  会游
            ....
        我们抽取一个共性 动物会: 会吃  会睡。  跑不一定,叫也不一定 如果不是所有动物都会叫,那么叫这个方法不会抽象到动物类中
        我们从小到大在脑子中建立了很多的类,这些个类都不是孤立的,我们早就完成了抽象父类的工作了
        交通工具 -- 车
                     |--汽车
                          |-- 小轿车
                                |-- 跑车
                                    |-- 兰博基尼
                                    |-- 法拉利
                                           |-- 法拉利918  (具体到一辆汽车对象)
                                    |-- 阿斯顿马丁
                                |-- suv
                                |-- ...
                          |-- 大客车
                          
                     |--火车
                 -- 船
                 -- 飞机
        面向对象就是把生活中的一些特性移植到了计算机领域中,
        于是我们在写类的时候同样可以像生活中那样定义两个类之间的继承关系    
    
    格式:  public class 子类名称  extends 父类名称{}
           继承的关键字: extends
     
    **案例demo1 
    代码描述: 
        public class Test2 {
            public static void main(String[] args) {
                //4  我们实验一下 狗中有没有吃方法  先创建狗对象  调用吃方法
                Dog  d=new Dog();
                d.eat(); //打印出 Animal eat
                //5  总结狗类作为动物的子类 从父类中继承到了eat() 方法
            }
        }
        class Animal{
            //1 父类是在子类中把共性抽象出来的  反推回来 父类中的属性和方法 其实也是子类中的属性和方法
            //2 定义一个吃方法  为什么能把吃方法定义在动物类中,因为我考察过吃这个方法 是所有动物的共性,
            // 所以我把吃这个方法提炼到父类中 从而是个动物就会吃
            public  void eat(){
                System.out.println("Animal eat");
            }

        }
        // extends  表示两个类之间的继承关系  说明Animal是Dog的父类 ,反之就是Dog是Animal的子类
        class  Dog extends  Animal{
            //3 狗是动物的子类 或者说狗是特殊的动物  那狗肯定也是会吃。 所以在这个程序中狗类有没有吃方法
        } 
================================================================================================================
2 继承的好处和弊端
    好处: 1提高代码的复用性
          2提高了代码的维护性
          3类和类之间产生联系(多态前提)
    
    弊端: 1侵入性
          2降低了代码的灵活性,继承关系导致了子类必须拥有父类继承给的属性和方法,让子类多了约束
          3增强了代码的耦合性

    应用场景:    类和类之间存在共性的内容 并产生了is_a的关系 就可以考虑使用继承来优化代码 
    
    ** 案例demo2
    命题: 那么我们说父类的所有属性和方法会继承给子类???  继承有什么用--->很容易的实现可扩展性
        class Animal{
            //父类的eat方法私有
            private  void eat(){
                System.out.println("Animal eat");
            }
        }
        class  Dog extends  Animal{
            public  void  method(){
                eat();   //假设我继承了父类的eat方法   我就可以在本来类内部的其他方法中调用。
                //结果是报错的 不能调用,
            }
        }
        public class Test2 {
            public static void main(String[] args) {
                //创建子类对象 调用子类method方法
                Dog  d=new Dog();
                d.method(); 
            }
        }
        
    *****子类能继承父类的所有方法和属性类图 
        
        从根本上说就是封装的问题  比如你们家有个保险箱里边有500W,你父母交给你保管了,也就相当于你拥有500w 但是你没有保险箱密码
        父类的所有属性和方法都能继承给子类-->没错,子类的范围包括父类的所有属性和方法
        父类的私有属性和方法没有继承给子类-->也没错,站在子类角度访问不到父类的私有成员,就如同于没有继承一样
        不同人对继承二字的理解不同
    
    解决方法: **案例demo3
        通过其他方法访问父类私有的属性和方法
            class Animal{
                //父类的eat方法私有
                private  void eat(){
                    System.out.println("Animal eat");
                }
                public  void m(){//定义一个公开方法去访问本类的私有方法
                    eat();
                }
            }
            class  Dog extends  Animal{//m方法会继承给子类dog
                public  void  method(){
                    m();   
                }
            }    
            public class Test2 {
                public static void main(String[] args) {
                    Dog d=new Dog();
                    d.method();
                }
            }
    结论:
        父类的私有属性和私有方法,子类是不能访问的,当然一些父类
        的私有属性可能可以通过相应的方法访问到,但是私有的方法似乎不能简单的访问,    
        在一个子类被创建的时候,首先会在内存中创建一个父类存储空间,然后在父类空间外部放上子类独有的属性,
        两者合起来形成一个子类的对象。
        所以所谓的继承使子类拥有父类所有的属性和方法其实可以这样理解,子类
        对象确实拥有父类对象中所有的属性和方法,但是父类对象中的私有属性和方法,子
        类是无法访问到的,只是拥有,但不能使用。就像有些东西你可能拥有,但是你并不
        能使用。所以子类对象是绝对大于父类对象的,        
================================================================================================================
3 继承中变量的访问特点
    1 在子类方法中访问一个变量
        查找顺序:  1 子类局部
                   2 子类成员
                   3 父类成员
        ** 如果子类和父类中出现了同名的成员变量 有就近原则 优先使用本类的
             
================================================================================================================
4 super
    1 this用法 (代表本类对象的引用)
        this.变量名    当本类局部变量和成员变量发生命名冲突的时候 this.变量名代表成员变量
        this.方法名    调用本类的成员方法
        this(参数表)   在调用本类的其他构造方法(用参数表区分调用的是哪一个构造方法) 必须是本类构造方法的第一句话
    
    2 super的用法: (代表父类对象的引用)
        super.变量名  调用父类中的成员变量
        super.方法名  调用父类中的成员方法
        super(参数表) 调用父类的构造方法
        
    super的用法:
      1第一个用法 super.   **案例demo4
        class Super{
            int a=10;
            public void  print1(){
                System.out.println("fu");
            }
        }
        class Sub extends Super{
            int a=50;
            public void  print(){
                int a=40;
                System.out.println(a);  // 40
                System.out.println(this.a); //50
                System.out.println(super.a); //10
                print1(); // zi
                super.print1(); //fu
            }

           public void  print1(){
               System.out.println("zi");
           }
        }
        
             
        
================================================================================================================
5 继承中构造方法的访问特点
      1子类的构造方法都会默认访问父类的无参构造方法//父类无参构造一般是必须的
          子类在初始化的时候有可能会使用到父类中的数据,如果父类没有完成初始化 子类将无法使用
      2通过构造super()访问父类的构造方法完成父类成员的初始化
      
      3 Object类是所有类直接或者间接父类 如果编写的类没有指定父类 那系统会默认继承Object类
         也就是说Object类的所有能被继承的属性和方法在其他类中都会有一份
         这也就是我写一个类没有写任何方法但是我创建对象之后,通过对象就有能调用的方法
         
         
    第二个用法 super()   **案例demo5
        public class Test {
            public static void main(String[] args) {
                 new Dog(); // 1 打印结果是 Animal() Dog()   默认行为 当创建子类对象的时候 首先要初始化父类 调用父类的构造方法
                 new Dog("abc");//2 打印结果是  Animal() Dog(String)
            }
        }
        class Animal {
            public  Animal(){
                System.out.println("Animal()");
            }
            public  Animal(String str){ //3 我这个有参数的构造方法什么时候能用
                System.out.println("Animal(String)");
            }
        }
        class Dog extends Animal{
            public Dog() {
                //super("abc");   //4 和this相似 用爱构造方法中 用来指定调用父类的哪个构造方法
                                  // 默认是super() 调用父类无参的构造方法  默认写在构造方法第一行
                System.out.println("Dog()");
            }
            public  Dog(String str){
                System.out.println("Dog(String)");
            }
        }
        总结:构造方法的第一句话有三种情况  this()  super()  或者是一个其他的语句
           所以如果一个构造方法中的第一个语句不是this 也不是super 系统会默认添加super() 无参。默认调用父类的无参构造方法 
        
        
        通过super给父类属性赋值   **案例demo6
        public class Test {
            public static void main(String[] args) {
                new Dog("大黄"); //1  调用子类的有参构造方法
            }
        }
        class Animal {
            private String name;  //子类如何给这个父类的私有属性赋值,
            public  Animal(){
                System.out.println("Animal()");
            }
            public  Animal(String str){
                System.out.println("Animal(String)");
                name=str;  //3  给父类的私有属性赋值
            }
        }
        class Dog extends Animal{
            public Dog() {
                System.out.println("Dog()");
            }
            public  Dog(String str){
                super(str); //  2 初始化父类 调用父类的有参构造
                System.out.println("Dog(String)");
            }
        }
        
        例子:猜对错
        class Super{
            public Super(String str){
                
            }
        }
        class Sub extends Super{
            
        }    
        这个例子是错误的  虽然看上去没有啥毛病 但是类中是有好多系统帮你添加的代码  是你看不见的
        class Super{
            public Super(String str){}
        }
        class Sub extends Super{
            
        }
        /*public  Sub(){
            super();
        }
        */
        //子类sub中默认有一个无参构造方法  方法的第一句话是一个super() 默认调用的是父类的无参构造  而父类有一个有参构造
        //系统不会默认添加无参数的构造方法 这个super() 找不到父类的无参构造方法  所以报错
    
    所以super 常常用来把构造参数传给父类 去设置父类的属性
    总结** 
       super的作用
        1  引用: 父类对象   在子类中访问父类被子类遮盖的属性或者被覆盖重写的方法
        2  在子类中指定调用父类的哪一个构造方法         
================================================================================================================
6:继承中成员方法的访问特点
    通过子类对象调用一个方法
       1 会现在子类成员范围查找
       2 如果没有就去父类成员范围查找       
       3 如果都没有找到报错

================================================================================================================
7 super内存图
   
================================================================================================================
8 方法重写
   方法的重写(OverRead) 也叫方法覆盖  
        ** 案例demo7
        class Animal {
            public void sleep() {
                System.out.println("sleep 8 hours");
            }
        }
        class Dog extends Animal{
            //从父类中继承了 睡觉方法  睡8小时   但是狗说了,我作为狗来说 是一种特殊的动物  我睡觉只睡6小时
            //但是我从父类中继承到的方法 只是个一般的实现  我需要自己在从新去实现下这个方法 
            //也就是把这个方法在子类中在写一遍 返回值 方法名 参数表不变
            //当我从写一遍的时候 就有再次实现一次的机会   狗说了睡6个小时。。。
            public   void  sleep(){
                System.out.println("sleep 6 hours");
            }
        }
        public class Test {
            public static void main(String[] args) {
                Dog d=new Dog();
                //d.sleep(); //创建一个狗对象 能调用睡觉方法  在父类中sleep方法是公开的子类继承到了 并且能访问  所以可以调用
                           //所以打印结果是睡觉8个小时
                d.sleep(); //当狗类把从父类中继承的方法覆盖掉之后   它在调用睡觉方法的时候 调用的是本身覆盖掉之后的方法,
                           //也就是它自身已经这个方法了 就不需要再去父类中调用其他的方法了。
                           //打印结果是 6个小时
            }
        }
        
    定义: 子类用特殊的方法实现替换掉父类继承给它的一般的方法实现
    语法要求:    访问修饰符相同或更宽泛       (返回值类型  方法名  参数表)不变        不能比父类抛出更对的异常    
     
    
    重载: 方法名相同参数表不同
    
    重写(方法覆盖): 继承体系同,子类出现了和父类一样的方法声明(修饰符相同或更宽,返回值类型,方法名,参数列表,不能比父类抛出更对的异常)
    
    下面这种情况不是重写  是重载  (父子类也有重载)
    class Animal {
        public void sleep() {
            System.out.println("sleep 8 hours");
        }
    }
    class Dog extends Animal{

        public   void  sleep(int a){
            System.out.println("sleep 6 hours");
        }
    }       
    访问修饰符限制  如果父类方法是public  那子类重写只能是public 

================================================================================================================
9:方法重写的注意事项:
     重写的注解: @Override
    1 私有的方法不能重写
    2 静态方法不能重写
    3 子类重写父类方法的时候访问权限必须大于等于父类
    
    


================================================================================================================
10 继承的注意事项

    1java支持单继承 不支持多继承,
    2java支持多级继承
    java 中的单继承      继承关系作为面向对象的一大特性 任何一个面向对象编程语言都必须支持继承的语法 
    java在继承这一块有一个独特之处就是只支持单继承----> 一个类最多只能有一个直接父类 
    格式:
       class  Student  extends  person  ---> 继承关键字extends后边只能出现一个类名
    那有没有可能有两个父类  
    A <------- B<--------C 
    //C的父类是B,B的父类是A,所以A 当然也是C的父类    但是并不违反单继承,C只有一个直接父类,A是间接的    
    动物<-------猫<-------波斯猫 
    //波斯猫是一种特殊的猫 但是波斯猫也是一种特殊的动物吧
    
    疑问点:
    为什么只是支持单继承,就是为了简单   JAVA对于C++有一个很重要的优势就是java的简单性
                          A
                         / \
                        B   C
                       / \   \
                      D   E   F 
                     /   / \  / \
                    G    H  I J  K
    每个类都只有一个直接的父类    这样的话这个继承关系就形成一个树状结构    A 就是根     树状关系是比较简单的关系     好比家谱
    但是C++多继承形成的关系是一张图   图结构就比较复杂了
    从上边这个关系看树结构从A到B 只有一条路  但是图结构 可能就不止一条路  就复杂多了
    
    多继承举例图**********    
    **总结** 所以说单继承就是一个类只有一个直接父类  类之间会形成简单的树状结构    

================================================================================================================
11 老师和学生
    //父类 person
    public class Person {
        private  String name;
        private  int  age;

        public Person() {
        }

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        //get/set
    }
    
    //子类 teacher
    public class Teacher extends Person{
    //    子类不能访问父类的私有属性 但是通过super访问父类的构造方法从而给属性赋值
    //    public Teacher(String name, int age) {
    //        this.name=name;
    //        this.age=age;
    //    }

        public Teacher() {
        }

        public Teacher(String name, int age) {
            super(name, age);
        }

        //成员方法
        public void teach(){
            System.out.println("仓井玛利亚.teacher");
        }
    }
================================================================================================================
12 猫和狗
    课堂练习:
  
================================================================================================================
13 packge 修饰符
   1 什么是包
        java程序是以类为单位组成的
        比如咱们用java做个计算器 。扫雷 可能写四五个类就足够了,但是大型的应用,如经常
        用的jd商城,天猫商城 这些个网站功能很复杂 用java写个千儿八百的类应该是有的,
        那么这么多类我们应该通过一个有效的方式把他们组织起来,
        这么多类编译完毕之后对应很多个.class文件。
        要是有几百几千个文件 你会用什么方式把这些类管理起来。--->目录

        你的电脑硬盘上可能有几万 甚至于几十万个文件, 你能想象到这些文件都放在C盘根目录是什么局面呢
        文件多了 我们自然会按照文件的功能  文件的类型去分门别类的放到很多的目录中
        目录中还能有子目录
         
        类似当我们项目中类变多的时候我们也会用包的概念把他们组织起来 
        包的概念和文件系统中目录的概念是可以划等号的 一个包就对应着一个目录
        目录中有子目录 那么包中可以有子包。
        那么我们在写类的时候写代码的时候就应该描述一下 这个类是属于哪一个包的 
        所以包的本质就是文件夹
        
        java描述包用一个关键字 package 
        package  包名;  //用这个语句来告诉别人这个文件中的类属于哪一个包
        特点;必须是整个程序的第一句话 当我们描述了一个类属于某个包的时候 就应该用目录的概念把他们管理起来
        package p1;

        public class Demo{
            public  static void main(String args []){
                System.out.println("Hello World");
            }
        }    
        编译:javac Demo.java 生成Demo.class
        运行:java Demo  报错?
        
        现在Demo类属于p1包 那么编译后的.class文件 应该放在一个名为p1的目录里边
        javac 编译 
        java 类名  全类名
        我们运行一个类格式是:
            java 类名(类名指的是一个类的全名,如果一个类有了包就好像一个人有了姓一样 全名肯定是姓+名 就是包名+类名)
                                   例子:喊一个人的名字叫德华;不光有刘德华 还有马德华
        正确运行:java p1.Demo
        那么虚拟机会怎么做呢 首先会根据你的classpath(.),先找当前的目录下找p1的子目录并在这个子目录中找Deme.class 如果找到了
        会先检测下Demo.class的第一行是不是 package p1; 看看结构正确不正确
    
        那么当前目录是那个呢 就是在哪个目录下能找到p1子目录就在哪个目录下运行  (你在p1包中运行也不行)
        
        刚才讲包中可以有子包
        package p1.p2.p3.p4.p5.p6;

        public class Demo{
            public  static void main(String args []){
                System.out.println("Hello World");
            }
        }    

        编译:javac Demo.java 生成Demo.class  要放在文件夹P6中
        运行:java p1.p2.p3.p4.p5.p6.Demo
        
        简单方式:  javac -d空格.空格 类名.java
        解释: 在编译的时候用添加 -d 参数 它的意思是生成的.class文件自动的按照包结构去存放  //编译器会自动帮你创建包结构
              那么创建的包结构放那里 你应该也一并指定吧  在-d后边 加 .  意思是放在当前目录下
    
        实际开发:
            在实际的企业开发当中 任何一个类都会放在对应的包中  所以实际上一般的java代码永远都是 package打头
            不写也是可以 那么编译之后的.class文件是放在当前目录下的  但这不是一个好的习惯 它是和企业开发习惯相违背的
            当一个类有了包之后 在使用这个类的时候 定是要  包名.类名  这种写法叫 [全限定名]
        
        
                 
    2 怎么分包
        原则: 不同功能的类,放在不同的包中  
              相同功能的类,放在相同的包中
        例举
        public static void main(String[] args) {
            //1甲方出钱让你写软件    就写一个helloworld 给你2000
            System.out.println("HelloWorld");
            
            FileReader fr=null;
            BufferedReader bf=null;
            String s=null;
            try {
            //2 过了几天甲方 又提了一个需求,你把这句话存在一个文件中,想换打印内容,就直接修改文件就行
                //程序免费升级
                fr=new FileReader("1.txt");
                bf=new BufferedReader(fr);
                s= bf.readLine();
                bf.close();
                System.out.println(s);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //3  需求又来了,  想把字符串倒叙  软件升级
            StringBuilder sb=new StringBuilder(s);
            String s1 = sb.reverse().toString();
            System.out.println(s1);
            //4 需求又来了, 要翻译成汉语, 怎么做 (弄一个非常大的map k 是单词, v是汉语)
            Map<String,String> map=new HashMap<>();
            map.put("Hello","你好");
            map.put("World","世界");

            String[] ss = s.split(" ");
            for (int i = 0; i < ss.length; i++) {
                String s2 = map.get(ss[i]);
                ss[i]=s2;
            }
            //拼接
            StringBuilder sb2=new StringBuilder("");
            for (String s2 : ss) {
                sb2.append(s2);
            }
            s=sb2.toString();
            System.out.println(s);

            /*
              后边可能还有无数个需求要提, 从代码角度讲,每当一个需求过来的时候我们都是在原来的代码上进行了修改
              结果就是代码越改越乱, 但是一个代码不会无限的乱下去,等到了看不懂的时候就不会再往下乱了,
              比如前50个需求是在同一个代码上修改的,等第51个需求来的时候我发现 如果还在原来的代码上修改
              不如另起山头从写一个新代码, 这还不是最可怕的,  现在还是我一个人在改, 还有可能改的人不一样,
              我写了40个需求离职了,后边是新来的人再接着写10个需求也离职了, 那些第51个需求的人看到的代码是2个人写的
              那就更没法看了,
                写代码人水平参差不齐,自己写代码还不是最痛苦的,看别人写的代码才是真痛苦, 看水平差的人写的会很生气
                一边看一边骂街,  看水平高的人写的也很难受,看不懂!!

              需求变化,带来了软件行业的进步, 计算机是工具,人类对工具的依赖是没有止境的,这也是人类社会发展很本源的一个
              动力,人类如果满足于骑马就不会发明汽车了,如果只满足汽车的速度,就不会有火车飞机了,
              所以人对软件的要求永远是在不断变化的
                比如qq  最早的时候 只能是聊天, 加好友消息验证都没有, 慢慢就加了好多功能, 群聊,分组,空间
                小游戏, 语音视频, 文件传输, 等到现在已经是一个很大的软件了,
               这么多功能的添加是因为人们对这个平台的需求在膨胀,所以需求变化促进了软件的发展.

              那软件为什么会死亡, 是在你需求变化的时候总是在原有的基础上去改写,直到改无可改(越改越乱), 
              等下次需求来了,只能推到从写的程度,
              如果我们能够在不改写,或者改动很少的前提下还能满足需求的变化,是不是就找到了代码长存的办法

              于是人们就提出了一种设计原则----开闭原则  (一个软件写好后对修改关闭,对扩展开放)
              比如咱们电脑内存不够了添加一块新内存条.

              那具体怎么做呢 
              1 软件要各司其职, 原有的系统职责要划分很清楚,当某个模块要扩展的时候很容易找到  
              2 弱耦合性: ABC三个模块,我扩展一个D模块替换C模块,然后对AB模块没有影响         
              3 重用性
              4 可扩展
              
              他们对应的就是
                1  封装对象
                2  接口 多态
                3  封装方法或者工具类
                4  继承实现扩展
             */
        }   

    我们用三层结构处理下输出helloworld
        1 职责划分
            1)获取数据
            2)处理数据
            3)显示数据
          一共要建立三个对象 我们分别给他起个名字
            1 dao
            2 service
            3 view
          这三者之间的关系是 
           view--->service---->dao  调用其中的方法
              
    案例讲解 分包:

================================================================================================================
14 import
        当我们用到类的时候都得写全限定名非常麻烦。
        假如:创建了一个A类 在A类中不可避免的会用到其他类,(java中代码是以类为单位的 类和类之间也不是相互独立的)    
            也许你写了个B类需要在A类中使用 在使用的时候A类中就要出现B类的名字 就得写B的全限定名   
      
        package p1;
        public class Demo{
            public  static void main(String args []){
                Date d;//这个类是我们安装jdk的时候那套类库中自带的类 sun公司写的 这样直接写是不行的 编译器看到这句话
                       //它知道你要用哪个Date类吗 有没有可能有好多包中有好多类都叫Date 所以你在用的别的类的时候一定要写全限定名
                java.utl.Date d2;       
                System.out.println("Hello World");
            }
        }     
        java.utl.Date;     如果用到好多类都这样写(全限定名) 会不会就成为了程序员的一种负担。
    
        给大家看一段代码
        package p1;
        public class Demo{
            public  static void main(String args []){
                Date d=new Date();       //java.util.Date
                List ls=new ArrayList(); //java.util.List   java.util.ArrayList
                File f=new File("");     //java.io.File
                System.out.println("Hello World");
            }
        }
        上边这些类你在用到的时候都得写包名
    
        怎么解决这个问题:java给我们提供了一个语法叫包的引入 import  
        格式:import 全限定名
                      
        package p1;
        import java.util.Date; //这句话的意思就是在这个类中如果用到Date这个类  都指的是java.util.Date类  相当于声明
        import java.util.List;
        import java.util.ArrayList;
        package p1;
        public class Demo{
            public  static void main(String args []){
                Date d=new Date();  
                List ls=new ArrayList(); 
                File f=new File("");
                System.out.println("Hello World");
            }
        }
    
        import java.util.Date; 
        import java.util.List;
        import java.util.ArrayList;
    
        或者把这三句话写成 import java.util.*; //* 是个通配符 表示这个包中的所有类,不包括子包、
                          错误写法: java.*.*   //人可以懒 但是不能懒成这个样子
    
        不写import行不行   是可以的 那就手勤快点 每次都写类的全限定名      
        其实在我们最开始学的hello world中也有类

        package p1;
        public class Demo{
            public  static void main(String args []){
                System.out.println("Hello World");
            }
        }    
       String  System其实都是类  用的时候前边没有带包名  类上没有写import 
       这两个类属于同一个包     
       java.lang 包   所以  java.lang.String  java.lang.System   
       这个包是java的语言包  是任何一个程序都离不开的最基础的一个包  所以系统会默认的为每一个程序加上imoprt java.lang.*
       只有这个包是自动引入的    
        ***
        总结: import 的作用是 避免重复的写包名,让我们的代码更加的简洁 少些一些啰嗦的东西,增加开发速度  
               当然你还要明白一个概念,不是一定要非写import,也可以每个类都去写全限定名。
        ***
        总结:  一个java程序的最基本的程序结构是什么样的 按照顺序
                package  0-1    0或者一个
                import   0-N    0或者多个
                class    1-N    1或者多个  公开类只能有一个

================================================================================================================
15 权限修饰符
    private  default  protected public  访问修饰符,它们描述的是一个访问权限的
         那么具体修饰符能修饰什么, 属性  方法   构造方法   局部变量不用修饰符
        private        属性     方法    构造方法    
        default        属性     方法    构造方法    类
            如果一个类、类属变量及方法没有用任何修饰符
          (即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)
        protected      属性     方法    构造方法
        public         属性     方法    构造方法    类

     从严逐渐放宽
    private  私有        本类内部访问 
    (default)默认        本类内部访问+同一个包下的类能访问
    protected 受保护的   本类内部访问+同一个包下的类能访问+不在同一个包下的子类能访问
    public   公开 

================================================================================================================
16 final  最终的
    1 修饰变量: 局部变量,实例变量  类变量  比static多修饰了一个局部变量  当变量被final修饰之后叫常量   变量一旦赋值不能改变
        //修饰成员变量
        class ClassA{
            //final int a=10;  //1 final修饰的成员属性必须手动初始化赋值  在定义时直接赋值
            final int a;
            public ClassA(int a) {//2 声明时不赋值,在构造方法中进行赋值 final成员变量在(构造器)结束之前一定要被赋予一个明确的值
                this.a=a;
            }
        }
        class ClassB {
            public final String str;
            {
                this.str = "mark";//3 声明时不赋值,在构造代码块中进行赋值
            }
        }
        
        public class demo1 {
            public static void main(String[] args) {
                  final int b=12; //修饰局部变量
                  //b++;//error
            }
        }
        
        //总结     对于一个final修饰的属性来说  赋值的机会太宝贵了,系统一定是会留给你的
        //        所以对于final属性来讲系统不会提供默认值
        
    2 修饰方法/类
    public class demo2修饰方法和类 {
        public static void main(String[] args) {

        }
    }
    final class Class1{
        int a=0;
    }
    /*
    class  Class2 extends Class1{//报错
        public final void m1(){
            System.out.println("aa");
        }
    }
    class Class3 extends Class2{
        public final void m1(){ //报错
            System.out.println("bb");
        }
    }
    */
    //总结: 被final修饰方法不能被子类重写
    //      被final修饰的类不能被子类继承
    //      被final修饰的引用变量所保存的地址值不能改变  但是所指向的对象的内容是可以改变的
    //    问:  被final 修饰的类中所有的方法默认都是final的
    //    答: 不仅对 而且是一句绝对的废话,这个类连子类都没有所以都谈不到重写。
    
================================================================================================================
18 static
    1概念:  静态的意思 是一个修饰符 
    2修饰范围:  成员方法 成员变量  代码块
        被static修饰的成员变量叫  静态变量
        被static修饰的成员方法叫  静态方法
        被static修饰的代码块叫    静态代码块
    
    3特点:  被static修饰的成员 会被该类的所有对象赋值
            被static修饰的成员 会随着类的加载而加载
            被static修饰的成员 可以通过类名.的形式调用
            (而且以后的用法也只是通过类型.调同 不会去创建对象在调用)
     
    4 详细讲解:
        1 修饰属性
            静态变量也叫类变量-->全类公有   直接用类名访问   (类名.属性名)
            class  MyClass{
                int  a=10;
                static int  b=20;
            } 
            public static void main(String[] args) {
                MyClass m1=new MyClass();
                MyClass m2=new MyClass();
                m1.a++;
                m1.b++;
                System.out.println(m2.a);  //判断 m1 m2 分别是不同的对象有各自的a属性  所以m2的a值现在还是10
                System.out.println(m2.b);  //结果是21  因为b是静态的
                /*
                  m1 m2分别是不同的对象 当m1的对象的a属性+1的时候  m2对象的a属性还是原来的10
                  但是b是static修饰的就是一个静态变量 
                  官方说话就是类变量  -- 类变量不在属于某一个对象是全类共有的  也就是说m1 m2 共有一个类变量
                  所以当m1对象对类变量b进行+1之后  m2对象去访问的时候已经是21了
                */
                //是全类共有的  直接用类名.访问
                System.out.println(MyClass.b);
                
                //同样也能访问到  我们应该想到静态b属性是什么时候才有的,
                //我们知道实例变量是在创建对象的时候才初始化赋值(不创建对象实例变量是不存在的)
                /*
                  很显然肯定不是在创建对象的时候初始化的b属性,那静态变量b是什么时候有的呢 ----->类加载
                  
                  我们说数据存在的基础是必须把虚拟机启动起来,把数据存在虚拟机的内存中,
                  虚拟机启动以后在程序执行的过程中肯定会用到很多的类
                  比如JVM开始执行代码 遇到了代码 new A(); 这是让JVM创建一个A类的对象,
                  虚拟机得琢磨什么是A类啊,怎么去创建对象,A的父类是谁
                  它的无参构造方法需要干什么,有那些个属性需要初始化。
                  虚拟机无法获知类的信息。当虚拟机第一次遇到一个类的时候,不知道这个类是个
                  什么东西,无法使用这个类。类的信息包括(它的父类,属性 方法  构造方法  分别都干了些什么)。
                  只有当虚拟机全部掌握了这个消息之后才能更好的去创建这个类,这个类的信息在  A.java这个类中都能找到。
                  但是虚拟机不认识A.java文件,虚拟机认识的是A.class文件,这个
                  文件中存着类的全部信息,虚拟机只要把这个文件中的内容读进内存就能知道类的信息了,就能很好的创建A对象了
                  但是第二次创建A对象呢  需要在从新读一遍吗。虚拟机不会那么笨,以前见过一次了,
                  它就会把这个类的信息读入虚拟机内存中保存起来以后不不用再去读取了。
                  所以这个.class的读取一般来说只会做一次。而且是在第一次用到这个类的时候。这个过程就是类的加载
                  
                  听完稀里糊涂的。。。
                  比如我让你给我做个骰子  你要先知道骰子是什么样子的有什么特点
                  
                  什么叫类加载 简单说:
                    当虚拟机第一次使用一个类的时候,需要通过classpath找到这个类对应的.class文件,
                    把这个文件中包含的类的信息读取到虚拟机内存并保存,一般来说一个类只会被加载一次  
                  类变量是在类加载的时候分配空间(这个时候还没有对象) 并且初始化的。
                  
                  类加载的时机:
                     通俗讲:  1 创建类的对象的时候  也就是new
                               2 访问类的静态成员(静态属性,静态方法)
                               3 加载子类必须先加载父类
                               4 如果仅仅声明引用是不会发生类加载  Student s;
                    
                                局部变量-|
                      |--属性|--实例变量-| 变量           
                    类|      |--类变量  -| 
                      |
                      |--方法 
                      
                      我们把属性和方法叫做成员 
                      有些人认为成员变量就是属性  有些人认为成员变量就是实例变量 所以没有一个确切的说法 尽量回避这种叫法 
                      */
           }
        
        2 方法
            静态方法:直接用类名调用   类名.方法名()   非静态方法需要创建对象去调用
            class  MyClass{
                int  a=10;
                static int  b=20;
                public  static  void print(){
                   System.out.println("喝喝");
                }
                public void m1(){
                    System.out.println("i am  m1");
                }
            }
            public class Test {
                public static void main(String[] args) {
                    //调用静态方法
                    MyClass.print();
                    //调用非静态方法
                    MyClass mc=new MyClass();
                    mc.m1();
                    mc.print(); //也可以调用 但是这不是找费事吗
                }
            }
            //看下面一个例子
            
            class  MyClass{
                int  a=10;
                static int  b=20;
                public  static  void print(){
                    //System.out.println(a);  //不能直接访问 类加载之后静态成员初始化,但是非静态的成员还没有初始化
                    System.out.println(b);
                    //但是我可以创建对象去访问
                    MyClass mc=new MyClass();
                    System.out.println(mc.a);
                }
            }
            
            public class Test {
                public static void main(String[] args) {
                   MyClass.print(); // 访问类的静态成员(静态属性,静态方法)  发生了类加载
                }
            }
            
            
            总结:在静态方法中只能访问类的静态成员,
                所以说我们刚开始学方法的时候 定义的都是静态方法,都是在main方法中调用的  主方法也是个静态方法
            
        3 初始代码块
             静态初始代码块中的代码 在类加载的时候执行一次
            public class Test {
                public static void main(String[] args) {
                        new MyClass();  //2 会先打印  hehe  在打印 MyClass();
                }
            }
            class  MyClass{
                //1 这个代码块是初始代码块 不属于任何方法  是对象的一个成员 在对象初始化的时候 
                //要先初始化属性,也会把初始化代码块执行以下 再调用构造方法
                // 动态初始化代码块  很少见
                {
                    System.out.println("hehe");
                }
                public MyClass(){
                    System.out.println("MyClass()");
                }
            }
            -----------------------------------------
            //静态初始化代码块
            class  MyClass2{
                //静态很常见 执行时机: 在类加载的时候执行一次  
                
                static{
                    System.out.println("hehe2");
                }
                public MyClass2(){
                    System.out.println("MyClass()2");
                }
            }
            
            public class Test2 {
                public static void main(String[] args) {
                        new MyClass2(); 
                        new MyClass2();    
                        //会先打印hehe  也只会打印一个 静态代码块是类加载的时候执行的 你第一次创建对象的时候需要类加载
                        //第二次创建对象就不需要类加载了     MyClass()2会打印两个  
                        //所以如果有需求需要某些代码在所有代码执行执行之前执行就可以写在static{}中
                        //常常用来加载配置文件    设置配置信息            
                }
            }
        
================================================================================================================
19 static 访问特点
        1 静态方法中只能访问静态成员(属性,方法)
           静态是随着类的加载而加载优先于对象
           非静态的属性和方法依赖于对象才能使用
           (main方法中 能直接调用的方法只有静态方法)   
        2 静态方法中可以使用静态成员,也可以使用非静态成员(但是需要new 对象)
        3 静态方法中 没有this关键字(this 代表当前对象 随着对象的存在而存在)
    
    
    final 修饰的类变量赋值
        //被final static修饰的变量
        //1、在定义时直接赋值
        class ClassC {
            public static final String str = "mark";
        }
        //2、在静态代码块里面进行赋值
        class ClassD {
            public static final String str;
            static {
                str = "mark";
            }
        }
        //总结
        /*
         总结:     对于final属性系统不会提供默认值
                  如果final修饰实例变量 可以在初始化属性或者构造方法,动态代码块中赋值
                  如果final修饰类变量   可以在初始化属性或者是静态代码块中赋值
        */

        //tips
        class ClassE{
            final int a=10;
                /*
                这个代码写是对的 但是这样写好不好, 如果真这样写绝对是一种缺心眼的表现,  我们分析  a是实例变量  每个对象都有自己的一份
                假设我有100W个对象,那么每个对象都有一个a都等于10 而且不能改变,既然每个人都有且一样 那不是在浪费存储空间吗
                所以我们能不能都用同一个10呢  所以针对这种情况我们可以
                static  final  a=10;  这样写才会经济  才不会浪费存储空间  或者你用构造方法赋值,那样每个人的值就不一样了
                其实还有问题; 应该写成 static final A=10;
                回忆下java中标识符的书写习惯
                  包名全小写  类名单词首字母大写  变量名和方法名首单词字母小写后边每个单词首字母大写  常量名全大写单词之间用下划线连接
                 */
        }    
    
    //静态方法能被重写吗    
    class  Super{
       public  static void method(){
           System.out.println("Super");
       }
    }
    class sub extends Super{
        
        public  static void method(){
            System.out.println("sub");
        }
    }
    public static void main(String[] args) {
        //子类通过类名调用静态方法
        sub.method();
        //通过子类对象调用静态方法
        sub s=new sub();
        s.method();
        //可以通过对象去调用静态方法
        Super sp=new Super();
        sp.method();
        //可以通过类名去调用静态方法
        Super.method();
    }
    结论:java中静态属性和静态方法是没有被重写(overwrite)而是被隐藏.
    原因:
    1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。
      如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。
      如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,

    通常,在一个类中定义一个方法为static,那就是说,无需本类的对象即可调用此方法,关于static方法,声明为static的方法有以下几条限制:
        它们仅能调用其他的static 方法。
        它们只能访问static数据。
        它们不能以任何方式引用this 或super。
    无论是static修饰的变量,还是static修饰的方法,我们都知道他们是属于类本身的,
    不是属于某一个对象的,当声明一个对象时,并不产生static变量和方法的拷贝。也就是说,
    用static修饰的变量和方法在类加载的时候,只分配一块存储空间,所有此类的对象都可以操控此块存储空间;

    注:这里要说明的时,当子类没有与之同名的static变量(或方法时),
    子类的对象也可以操控这块内存空间。但是子类并没有继承父类中static修饰的变量和方法。
    因为static修饰的变量和方法是属于父类本身的。
     
    但是当父类中有一个static修饰的变量(或方法),而子类中也存在一个同名的static修饰的变量(或方法)时 
    子类和父类中同名的static变量和方法都是相互独立的,并不存在任何的重写的关系。    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ོ阿超@ꦿএ᭄゛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值