java基礎回顧

基本數據類型和引用數據類型的區別

  • 基本數據類型
    聲明時直接在棧內存中開闢空間,並直接在當前內存中存放數據,賦值時傳遞的是變量中的值,總的來講,基本數據類型是傳值的。
  • 引用數據類型
    會將實際的數據存放在堆內存中,同時,在棧內存中聲明一個數組名或對象名,存放着在堆內存中的是地址;
  • 二者關係圖
    圖片描述
  • 二者對比圖
    ![圖片描述
  • 二者建立圖
    圖片描述
  • 賦值運算符(=)的做用圖
    圖片描述

值傳遞和引用傳遞區別

  • 值傳遞
    在方法的調用過程當中,實參把它的實際值傳遞給形參,此傳遞過程就是將實參的值複製一份傳遞到函數中,這樣若是在函數中對該值(形參的值)進行了操做將不會影響實參的值。由於是直接複製,因此這種方式在傳遞大量數據時,運行效率會特別低下。
  • 引用傳遞
    引用傳遞彌補了值傳遞的不足,若是傳遞的數據量很大,直接復過去的話,會佔用大量的內存空間,而引用傳遞就是將對象的地址值傳遞過去,函數接收的是原始值的首地址值。在方法的執行過程當中,形參和實參的內容相同,指向同一塊內存地址,也就是說操做的其實都是源數據,因此方法的執行將會影響到實際對象。(注意:這裏所說的是改變源數據的內容,如:改變的對象的屬性)
  • 實例java

    public class Example {    
        String str = new String("hello");    
        char[] ch = {'a', 'b'};
        
        public static void main(String[] args) {
            change(str, ch);
            System.out.println(str);    //輸出:hello
            System.out.println(ch);    //輸出:cd
        }    
            
        public void change(String str, char[] ch) {        
            str = "ok";
            ch[0] = 'c';    //改變數據源內容
        }
    }
  • 過程分析圖:
    圖片描述

經常使用類:java.lang.Enum

  • 使用場景
    當咱們須要限制一系列變量的時候,一般想到數組或者集合;其實不少時候咱們須要限定變量須要作的事情不少,或者說若是被限制的變量能夠作其餘事情的話就更好了,而不是單純的一個變量,那麼,枚舉的做用不只僅可讓你使用限制在一個enum中的變量,而且這些變量的靈活性和拓展性很好。數據庫

    public enum WeekEnums {
            //注:枚舉寫在最前面,不然編譯出錯
            Sunday,
            Monday,
            Tuesday,
            Wednesday,
            Thursday,
            Friday,
            Saturday;
     
            private static String getWeek(WeekEnums weekEnums) {
                String week = null;
                switch (weekEnums) {
                    case Sunday://星期天
                        week = "星期天";
                        //此處寫邏輯處理代碼
                        
                        break;
                    case Monday://星期一
                        week = "星期一";
                        //此處寫邏輯處理代碼
                        
                        break;
                    case Tuesday:// 星期二
                        week = "星期二";
                        //此處寫邏輯處理代碼
                        
                        break;
                    case Wednesday://星期三
                        week = "星期三";
                        //此處寫邏輯處理代碼
                        
                        break;
                    case Thursday:// 星期四
                        week = "星期四";
                        //此處寫邏輯處理代碼
                        
                        break;
                    case Friday://星期五
                        week = "星期五";
                        //此處寫邏輯處理代碼
                        
                        break;
                    case Saturday://  星期六
                        week = "星期六";
                        //此處寫邏輯處理代碼
                        
                        break;
                }
                return week;
            }
        }
    //獲取方式:
    String weekday = WeekEnums.getWeek(WeekEnums.Friday);

經常使用類:java.math.BigDecimal

  • 使用場景
    float和double類型的主要設計目標是爲了科學計算和工程計算。他們執行二進制浮點運算,這是爲了在廣域數值範圍上提供較爲精確的快速近似計算而精心設計的。然而,它們沒有提供徹底精確的結果,因此不該該被用於要求精確結果的場合。可是,商業計算每每要求結果精確,這時候就應該使用BigDecimal。

經常使用類:java.lang.ThreadLocal

  • 使用場景
    在多線程中,爲保證多個線程對共享變量的安全訪問,一般會使用synchronized來保證同一時刻只有一個線程對共享變量進行操做。這種狀況下能夠將類變量放到ThreadLocal類型的對象中,使變量在每一個線程中都有獨立拷貝,不會出現一個線程讀取變量時而被另外一個線程修改的現象。ThreadLocal是編程中空間換時間的體現。
  • ThreadLocal的內部結構編程

    1. 每一個Thread線程內部都有一個Map。
    2. Map裏面存儲線程本地對象(key)和線程的變量副本(value)。
    3. 每一個Thread線程內部都有一個Map。
    4. Thread對於不一樣的線程,每次獲取副本值時,別的線程並不能獲取到當前線程的副本值,造成了副本的隔離,互不干擾。
  • ThreadLocal的內部結構圖
    圖片描述
  • 實例數組

    //包含業務惟一標識的類
    public class Context {
        private String transactionId;
     
        public String getTransactionId() {
            return transactionId;
        }
     
        public void setTransactionId(String transactionId) {
            this.transactionId = transactionId;
        }
     
    }
    
    // 其中引用了Context類
    public class MyThreadLocal {
        private static final ThreadLocal<Context> userThreadLocal = new ThreadLocal<Context>();
        public static void set(Context user){
            userThreadLocal.set(user);
        }
        public static void unset(){
            userThreadLocal.remove();
        }
        public static Context get(){
            return userThreadLocal.get();
        }
        
    }
    
    //ThreadLocalDemo.java。生成並將業務標識設置到ThreadLocal中而後在業務方法中調用
    public class ThreadLocalDemo implements Runnable{
        private static AtomicInteger ai = new AtomicInteger(0);
        public void run() {
            Context context = new Context();
            context.setTransactionId(getName());
            MyThreadLocal.set(context);
            System.out.println("request["+Thread.currentThread().getName()+"]:"+context.getTransactionId());
            new BusinessService().businessMethod();
            MyThreadLocal.unset();
        }
        
        private String getName() {
            return ai.getAndIncrement()+"";
        }
     
        public static void main(String[] args) {
            ThreadLocalDemo tld  = new ThreadLocalDemo();
            new Thread(tld).start();
            new Thread(tld).start();
        }
     
    }
    public class BusinessService {
     
        public void businessMethod() {
            Context context = MyThreadLocal.get();
            System.out.println("service["+Thread.currentThread().getName()+"]:"+context.getTransactionId());
        }
     
    }

String

  • Q1:String s = new String("hollis");定義了幾個對象。
    A1:若常量池中已經存在」hollis」,則直接引用,也就是此時只會建立一個對象,若是常量池中不存在」hollis」,則先建立後引用,也就是有兩個。
  • Q2:如何理解String的intern方法?
    A2:當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字符串常量,若是有,則返回其的引用,若是沒有,則在常量池中增長一個Unicode等於str的字符串並返回它的引用;
  • 實例安全

    String s1 = "Hollis";
        String s2 = new String("Hollis");
        String s3 = new String("Hollis").intern();
    
        System.out.println(s1 == s2);    //輸出:false
        System.out.println(s1 == s3);    //輸出:true
  • 建立分析圖
    圖片描述
    String s3 = new String("Hollis").intern();,在不調用intern狀況,s3指向的是JVM在堆中建立的那個對象的引用的(如圖中的s2)。可是當執行了intern方法時,s3將指向字符串常量池中的那個字符串常量。
  • String不變性
    一旦一個string對象在內存(堆)中被建立出來,他就沒法被修改。注意:String類的全部方法都沒有改變字符串自己的值,都是返回了一個新的對象。若是你須要一個可修改的字符串,應該使用StringBuffer 或者 StringBuilder。不然會有大量時間浪費在垃圾回收上,由於每次試圖修改都有新的string對象被建立出來。
  • 實例
    定義一個字符串網絡

    String s = "abcd";

    圖解
    圖片描述
    使用變量來賦值變量數據結構

    String s2 = s;

    圖解
    圖片描述
    字符串鏈接多線程

    s = s.concat("ef");

    圖解
    圖片描述ide

  • 爲何設計成不可變:
    設計成不可變的主要目的是爲了安全和高效
  • 爲何數組有length屬性?
    首先,數組是一個容器對象,其中包含固定數量的同一類型的值。一旦數組被建立,他的長度就是固定的了。數組的長度能夠做爲final實例變量的長度。所以,長度能夠被視爲一個數組的屬性。
  • 爲何String有length()方法?
    String背後的數據結構是一個char數組,因此沒有必要來定義一個沒必要要的屬性(由於該屬性在char數值中已經提供了)。和C不一樣的是,Java中char的數組並不等於字符串,雖然String的內部機制是char數組實現的。(注:C語言中,並無String類,定義字符串一般使用char string[6] = "hollis";的形式)。
  • equals()和hashcode()之間關係
    在判斷兩個對象是否相等時,不要只使用equals方法判斷。還要考慮其哈希碼是否相等。尤爲是和hashMap等與hash相關的數據結構一塊兒使用時。
    一、若是兩個對象相等,那麼他們必定有相同的哈希值(hash code)。
    二、若是兩個對象的哈希值相等,那麼這兩個對象有可能相等也有可能不相等。(須要再經過equals來判斷)

關鍵字

  • 四種訪問控制區別圖
    圖片描述
  • 泛型中K T V E ? object等的含義
    E – Element (在集合中使用,由於集合中存放的是元素)
    T – Type(Java 類)
    K – Key(鍵)
    V – Value(值)
    N – Number(數值類型)
    ? – 表示不肯定的java類型(無限制通配符類型)
    Object – 是全部類的根類,任何類的對象均可以設置給該Object引用變量,使用的時候可能須要類型強制轉換,可是用使用了泛型T、E等這些標識符後,在實際用以前類型就已經肯定了,不須要再進行類型強制轉換。
  • 可變參數
    它容許一個方法把任意數量的值做爲參數。
    可變參數的工做原理
    可變參數在被使用的時候,他首先會建立一個數組,數組的長度就是調用該方法是傳遞的實參的個數,而後再把參數值所有放到這個數組當中,而後再把這個數組做爲參數傳遞到被調用的方法中。
    實例函數

    public static void main(String[] args) {
        print("a");
        print("a", "b");
        print("a", "b", "c");
    }
    
    public static void print(String ... s){
        for(String a: s)
            System.out.println(a);
    }

Comparable和Comparator

  • 做用
    用來作對象之間的比較的和對象排序。
    Comparator實例

    class Dog {
        int size;
    
        Dog(int s) {
            size = s;
        }
    }
    
    class SizeComparator implements Comparator<Dog> {
        @Override
        public int compare(Dog d1, Dog d2) {
            return d1.size - d2.size;
        }
    }
    
    public class ImpComparable {
        public static void main(String[] args) {
            TreeSet<Dog> d = new TreeSet<Dog>(new SizeComparator()); // pass comparator
            d.add(new Dog(1));
            d.add(new Dog(2));
            d.add(new Dog(1));
        }
    }

    Comparable實例

    class Dog implements Comparable<Dog>{
        int size;
    
        Dog(int s) {
            size = s;
        }
    
        @Override
        public int compareTo(Dog o) {
            return o.size - this.size;
        }
    }
    
    public class ImpComparable {
        public static void main(String[] args) {
            TreeSet<Dog> d = new TreeSet<Dog>();
            d.add(new Dog(1));
            d.add(new Dog(2));
            d.add(new Dog(1));
        }
    }

重載(Overloading)

  • 定義
    簡單說,就是函數或者方法有一樣的名稱,可是參數列表不相同的情形,這樣的同名不一樣參數的函數或者方法之間,互相稱之爲重載函數或者方法。
  • 實例

    class Dog{
        public void bark(){
            System.out.println("woof ");
        }
    
        //overloading method
        public void bark(int num){
            for(int i=0; i<num; i++)
                System.out.println("woof ");
        }
    }

重寫(Overriding)

  • 定義
    重寫指的是在Java的子類與父類中有兩個名稱、參數列表都相同的方法的狀況。因爲他們具備相同的方法簽名,因此子類中的新方法將覆蓋父類中原有的方法。
  • 實例

    class Dog{
        public void bark(){
            System.out.println("woof ");
        }
    }
    class Hound extends Dog{
        public void sniff(){
            System.out.println("sniff ");
        }
    
        public void bark(){
            System.out.println("bowl");
        }
    }
    
    public class OverridingTest{
        public static void main(String [] args){
            Dog dog = new Hound();
            dog.bark();
        }
    }
    
    //最終輸出:bowl

組合和繼承

  • 組合定義
    其實所謂的組合就是建立一個新類去調用已經建立而且調試好的類,那麼這個新類就能夠把它叫作是一個組合。例如:A類中有一個屬性對象B,那麼這個A類就是一個組合類。
  • 繼承定義
    經過extends的關鍵字,它能夠幫助咱們繼承,被繼承的類咱們稱做父類,也能夠叫作基類,超類都行,而繼承者咱們稱做子類或者派生類等等。
  • 二者比較
    圖片描述
    建議在一樣可行的狀況下,優先使用組合而不是繼承。由於組合更安全,更簡單,更靈活,更高效。繼承要慎用,其使用場合僅限於你確信使用該技術有效的狀況。一個判斷方法是,問一問本身是否須要重新類向基類進行向上轉型。若是是必須的,則繼承是必要的。反之則應該好好考慮是否須要繼承。

迭代與遞歸

  • 實例

    //Q1:n*(n-1)*(n-2) = ?
    
    //遞歸
    int factorial (int n) {
        if (n == 1) {
            return 1;
        } else {
            return n*factorial(n-1);
        }
    }
    
    //迭代
    int factorial (int n) {
        int product = 1;
        for(int i=2; i<n; i++) {
            product *= i;
        }
        return product;
    }
    
    //Q2:fib(n-1) + fib(n-2) = ?
    
    //遞歸
    int fib (int n) {
        if (n == 0) {
            return 0;
        } else if (n == 1) {
            return 1;
        } else {
            return fib(n-1) + fib(n-2);
        }
    }
    
    //迭代
    int fib (int n) {
        int fib = 0;
        int a = 1;
        for(int i=0; i<n; i++) {
           int temp = fib;
            fib = fib + a;
            a = temp;
        }
        return fib;
    }
  • 二者比較圖
    圖片描述
    從對比圖建議能不用遞歸就不用遞歸,遞歸多數均可以用迭代來代替。

反射

  • 使用場景
    用於動態代理和動態建立類,獲取對象全部屬性和方法(包含私有默認父類)。java中全部對象都是Class這個類的實例對象;經過Class這個類對象就能夠實現反射機制。
  • 優勢:反射機制就是增長程序的靈活性,避免將程序寫死到代碼裏。如struts中請求的派發控制,當請求來到時,struts經過查詢配置文件,找到該請求對應的action方法,而後經過反射實例化action,並調用響應method。
  • 缺點:對性能有必定影響
  • 得到Class的三種方式
    一、經過 Object 類中的 getClass() 方法,知道對象時用

    Person p1 = new Person();
    Class<?> c1 = p1.getClass();

    二、直接經過 類名.class 的方式獲得,該方法最爲安全可靠,程序性能更高,說明任何一個類都有一個隱含的靜態成員變量 class,

    Class<?> c2 = Person.class;

    三、經過 Class 對象的 forName() 靜態方法來獲取,用的最多,但可能拋出 ClassNotFoundException 異常,主要用於動態代理。

    Class<?> c3 = Class.forName("com.ys.reflex.Person");
  • 類加載圖
    圖片描述
    上圖可知:一個類在 JVM 中只會有一個 Class 實例,即咱們對上面獲取的 c1,c2,c3進行 equals 比較,發現都是true。
  • 經過反射獲取構造方法實例

    package fanshe;
     
    public class Student {
        
        //---------------構造方法-------------------
        //(默認的構造方法)
        Student(String str){
            System.out.println("(默認)的構造方法 s = " + str);
        }
        
        //無參構造方法
        public Student(){
            System.out.println("調用了公有、無參構造方法執行了。。。");
        }
        
        //有一個參數的構造方法
        public Student(char name){
            System.out.println("姓名:" + name);
        }
        
        //有多個參數的構造方法
        public Student(String name ,int age){
            System.out.println("姓名:"+name+"年齡:"+ age);//這的執行效率有問題,之後解決。
        }
        
        //受保護的構造方法
        protected Student(boolean n){
            System.out.println("受保護的構造方法 n = " + n);
        }
        
        //私有構造方法
        private Student(int age){
            System.out.println("私有的構造方法   年齡:"+ age);
        }
     
    }
    
    //測試類
    package fanshe;
    import java.lang.reflect.Constructor;
     
    public class Constructors {
     
        public static void main(String[] args) throws Exception {
            //1.加載Class對象
            Class clazz = Class.forName("fanshe.Student");
            
            
            //2.獲取全部公有構造方法
            System.out.println("**********************全部公有構造方法*********************************");
            Constructor[] conArray = clazz.getConstructors();
            for(Constructor c : conArray){
                System.out.println(c);
            }
            
            
            System.out.println("************全部的構造方法(包括:私有、受保護、默認、公有)***************");
            conArray = clazz.getDeclaredConstructors();
            for(Constructor c : conArray){
                System.out.println(c);
            }
            
            System.out.println("*****************獲取公有、無參的構造方法*******************************");
            Constructor con = clazz.getConstructor(null);
            //1>、由於是無參的構造方法因此類型是一個null,不寫也能夠:這裏須要的是一個參數的類型,切記是類型
            //2>、返回的是描述這個無參構造函數的類對象。
        
            System.out.println("con = " + con);
            //調用構造方法
            Object obj = con.newInstance();
        //    System.out.println("obj = " + obj);
        //    Student stu = (Student)obj;
            
            System.out.println("******************獲取私有構造方法,並調用*******************************");
            con = clazz.getDeclaredConstructor(char.class);
            System.out.println(con);
            //調用構造方法
            con.setAccessible(true);//暴力訪問(忽略掉訪問修飾符)
            obj = con.newInstance('男');
        }
        
    }
  • 獲取成員變量實例

    package fanshe.field;
     
    public class Student {
        public Student(){
            
        }
        //**********字段*************//
        public String name;
        protected int age;
        char sex;
        private String phoneNum;
        
        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + ", sex=" + sex + ", phoneNum=" + phoneNum + "]";
        }
        
    }
    
    //測試類
    package fanshe.field;
    import java.lang.reflect.Field;
    
    public class Fields {
     
            public static void main(String[] args) throws Exception {
                //1.獲取Class對象
                Class stuClass = Class.forName("fanshe.field.Student");
                //2.獲取字段
                System.out.println("************獲取全部公有的字段********************");
                Field[] fieldArray = stuClass.getFields();
                for(Field f : fieldArray){
                    System.out.println(f);
                }
                System.out.println("************獲取全部的字段(包括私有、受保護、默認的)********************");
                fieldArray = stuClass.getDeclaredFields();
                for(Field f : fieldArray){
                    System.out.println(f);
                }
                System.out.println("*************獲取公有字段**並調用***********************************");
                Field f = stuClass.getField("name");
                System.out.println(f);
                //獲取一個對象
                Object obj = stuClass.getConstructor().newInstance();//產生Student對象--》Student stu = new Student();
                //爲字段設置值
                f.set(obj, "劉德華");//爲Student對象中的name屬性賦值--》stu.name = "劉德華"
                //驗證
                Student stu = (Student)obj;
                System.out.println("驗證姓名:" + stu.name);
                
                
                System.out.println("**************獲取私有字段****並調用********************************");
                f = stuClass.getDeclaredField("phoneNum");
                System.out.println(f);
                f.setAccessible(true);//暴力反射,解除私有限定
                f.set(obj, "18888889999");
                System.out.println("驗證電話:" + stu);
                
            }
        }
  • 獲取成員方法實例

    package fanshe.method;
     
    public class Student {
        //**************成員方法***************//
        public void show1(String s){
            System.out.println("調用了:公有的,String參數的show1(): s = " + s);
        }
        protected void show2(){
            System.out.println("調用了:受保護的,無參的show2()");
        }
        void show3(){
            System.out.println("調用了:默認的,無參的show3()");
        }
        private String show4(int age){
            System.out.println("調用了,私有的,而且有返回值的,int參數的show4(): age = " + age);
            return "abcd";
        }
    }
    
    //測試類
    package fanshe.method;
    import java.lang.reflect.Method;
    
    public class MethodClass {
     
        public static void main(String[] args) throws Exception {
            //1.獲取Class對象
            Class stuClass = Class.forName("fanshe.method.Student");
            //2.獲取全部公有方法
            System.out.println("***************獲取全部的」公有「方法*******************");
            stuClass.getMethods();
            Method[] methodArray = stuClass.getMethods();
            for(Method m : methodArray){
                System.out.println(m);
            }
            System.out.println("***************獲取全部的方法,包括私有的*******************");
            methodArray = stuClass.getDeclaredMethods();
            for(Method m : methodArray){
                System.out.println(m);
            }
            System.out.println("***************獲取公有的show1()方法*******************");
            Method m = stuClass.getMethod("show1", String.class);
            System.out.println(m);
            //實例化一個Student對象
            Object obj = stuClass.getConstructor().newInstance();
            m.invoke(obj, "劉德華");
            
            System.out.println("***************獲取私有的show4()方法******************");
            m = stuClass.getDeclaredMethod("show4", int.class);
            System.out.println(m);
            m.setAccessible(true);//解除私有限定
            Object result = m.invoke(obj, 20);//須要兩個參數,一個是要調用的對象(獲取有反射),一個是實參
            System.out.println("返回值:" + result);
            
            
        }
    }
  • 經過反射運行配置文件內容實例

    public class Student {
        public void show(){
            System.out.println("is show()");
        }
    }

    配置文件以txt文件爲例子(pro.txt):

    className = cn.fanshe.Student
    methodName = show

    測試類:

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.Properties;
     
    public class Demo {
        public static void main(String[] args) throws Exception {
            //經過反射獲取Class對象
            Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
            //2獲取show()方法
            Method m = stuClass.getMethod(getValue("methodName"));//show
            //3.調用show()方法
            m.invoke(stuClass.getConstructor().newInstance());
            
        }
        
        //此方法接收一個key,在配置文件中獲取相應的value
        public static String getValue(String key) throws IOException{
            Properties pro = new Properties();//獲取配置文件的對象
            FileReader in = new FileReader("pro.txt");//獲取輸入流
            pro.load(in);//將流加載到配置文件對象中
            in.close();
            return pro.getProperty(key);//返回根據key獲取的value值
        }
    }

    當咱們升級這個系統時,不要Student類,而須要新寫一個Student2的類時,這時只須要更改pro.txt的文件內容就能夠了。代碼就一點不用改動

    public class Student2 {
        public void show2(){
            System.out.println("is show2()");
        }
    }

    配置文件更改成:

    className = cn.fanshe.Student2
    methodName = show2

序列化和反序列化

  • 序列化:對象序列化的最主要的用處就是在傳遞和保存對象的時候,保證對象的完整性和可傳遞性。序列化是把對象轉換成有序字節流,以便在網絡上傳輸或者保存在本地文件中。序列化後的字節流保存了Java對象的狀態以及相關的描述信息。序列化機制的核心做用就是對象狀態的保存與重建。
  • 反序列化:客戶端從文件中或網絡上得到序列化後的對象字節流後,根據字節流中所保存的對象狀態及描述信息,經過反序列化重建對象。
  • 本質上講,序列化就是把實體對象狀態按照必定的格式寫入到有序字節流,反序列化就是從有序字節流重建對象,恢復對象狀態
  • 使用場景
    一、永久性保存對象,保存對象的字節序列到本地文件或者數據庫中;
    二、經過序列化以字節流的形式使對象在網絡中進行傳遞和接收;
    三、經過序列化在進程間傳遞對象;
  • 缺點
    一、序列化後的碼流太大
    二、序列化性能太差
  • 實現要求
    只有實現了Serializable或Externalizable接口的類的對象才能被序列化,不然拋出異常!

待完善。。。

相關文章
相關標籤/搜索