首頁>Club>
5
回覆列表
  • 1 # 周林ZhouLin

    Java不支援運算子過載 = 小白也能學程式設計

    Java之所以不支援運算子過載,並不是如下原因:

    會使JVM變得複雜、效能下降:君不見C++內建運算子過載的能力?C++的效能在任何時代秒殺Java相信沒有爭議。便於靜態分析、工具化等:一葉障目、不見泰山。運算子過載只是一種動態特性,動態語言的形式化靜態分析方法已經有成熟的方法論。Java是面嚮物件語言:Ruby是比Java更徹底的面向物件的語言,然而它對運算子過載的支援非常優秀,在Ruby中一切都是物件,幾乎一切都可以override。

    不支援運算子過載的根本原因,是源自James Gosling設計Java的初衷:那就是要讓Java的學習門檻足夠低,這樣才能讓這個程式語言被更多的人使用,從而擁有最大的市場佔有率。

    Java誕生之前, 基本上是C/C++的天下。光C語言的一個指標,就嚇退了多少莘莘學子?C++引入更多的動態特性:多型、多重繼承、函式過載、函式重寫、運算子過載、泛型……這更不知道讓多少人望而卻步!

    正是在那樣的大環境下,James Gosling才萌生了“開發一個小白都能上手”的程式語言的念頭。

    運算子過載的底層思想並不是面向物件

    運算子過載的底層邏輯來自函數語言程式設計。它的祖師爺是Lisp,一個“從來被模仿、從未被超越”的神級語言。

    可以負責任地講,如今流行的Python、Javascript、Typescript、Go、Ruby、Haskell、Scala、Groovy等,在動態高階特性上都是在不斷模仿60多年前的Lisp。包括Java從誕生起就在鼓吹的垃圾回收等優點,全部都是“偷師”Lisp。有興趣的小夥伴可以自行下載Lisp的發明者——John McCarthy老爺爺1960年發表的GC論文。

    函式式語言的核心思想其實是數學。

    說得更白話一點:透過數學表示式描述問題,而不是人肉模擬解答過程。問題描述完了,也就解決了——執行時處理執行細節。

    說得更學院派一點:透過無狀態的函式加以其他最佳化特性,將這些函式元件進行拼接。

    看到這裡,估計有不少人要來拍磚:運算子過載看起來那麼複雜,明明可以定義方法或者函式來解決,除了裝逼格,沒有實用價值。

    筆者這裡迴應一下:數學本來就不是普通大眾擅長的,數學的目的就是用最簡潔的方式來解決最複雜的問題。所以函式式語言從誕生之初,就沒有想過要芸芸眾生。它追求的是大道至簡。

    這裡來看一個例子:計算一組資料(假設放在一個一維陣列中)的標準差。

    如果不採用函數語言程式設計,採用通常的面向過程或者面向物件的程式設計正規化,那麼只能:

    第一步,先透過迴圈體(for/foreach/while等),挨個遍歷求出平均值mean;

    第二步,再來一次迴圈,挨個求與mean的差值並平方,然後逐個累加得到平方合sumOfSquares;

    第三步,對sumOfSquares呼叫平方根函式,求出最終值standardDeviation。

    下面我們來進化一點:

    有基本函數語言程式設計概念的小夥伴可能會寫出如下的簡化正規化(這裡以Ruby為例):

    mean = a.inject {|x,y| x+y } / a.size

    sumOfSquares = a.map{|x| (x-mean)**2 }.inject{|x,y| x+y }

    standardDeviation = Math.sqrt(sumOfSquares/(a.size-1))

    但是真正的函數語言程式設計高手是會這樣寫的:

    第一步:寫一個通用的數學意義上的複合函式(f(g(x)) = f*g(x))的表達:

    module Functional

    def apply(enum)

    enum.map &self

    end

    alias | apply

    def reduce(enum)

    enum.inject &self

    end

    alias <= reduce

    def compose(f)

    if self.respond_to?(:arity) && self.arity == 1

    lambda {|*args| self[f[*args]] }

    else

    lambda {|*args| self[*f[*args]] }

    end

    end

    alias * compose

    end

    第二步:把計算標準差所需要的各個元素的數學表達列示好:

    sum = lambda {|x,y| x+y } # A function to add two numbers

    mean = (sum<=a)/a.size # Or sum.reduce(a) or a.inject(&sum)

    deviation = lambda {|x| x-mean } # Function to compute difference from mean

    square = lambda {|x| x*x } # Function to square a number

    第三步:像寫標準差的數學表示式一樣,一步到位:

    standardDeviation = Math.sqrt((sum<=square*deviation|a)/(a.size-1))

    總結

    Java之所以流行,並不是因為其語言設計得最優秀,相反地,在很多地方——比如泛型、Lambda、完全面向物件等設計上都存在不足。它的成功在於:揚長避短,把所有牛X的高階語言特性在一開始全部都拋棄,留一個最小核,然後透過營銷,大規模地培養本語言陣營的程式設計師,建立各種各樣的“輪子”,成就了巨無霸的生態;在站穩格局之後,慢慢地再逐步添加回來一些以前拋棄的其他語言的優秀特性——這是一種比較實用的策略,但是帶來的惡果就是:歷史包袱比較重,導致新特性很多時候是“半殘”的。

    回到運算子過載本身,對於高手,可以利用該特性寫出極具“魔性”、接近數學語言的程式碼,這樣的程式碼可以體現“極簡之美”——但是,一個不利影響就是:數學不好的小夥伴,不容易看得懂,也很難體會其中蘊含的“數學之美”。

  • 2 # 妙手書生朱聰132

    與C++不同,Java 不支援運算子過載。Java不能為程式設計師提供自由的標準算術運算子過載,例如+,-,*和/等。如果你以前用過C++,那麼Java與C++相比少了很多功能,例如Java不支援多重繼承,Java中沒有指標,Java中沒有引用傳遞。雖然我不知道背後的真正原因,但我認為以下說法有些道理,為什麼Java不支援運算子過載。

    1.簡單性和清晰性。清晰性是Java設計者的目標之一。設計者不是隻想複製語言,而是希望擁有一種清晰,真正面向物件的語言。新增運算子過載比沒有它肯定會使設計更復雜,並且它可能導致更復雜的編譯器, 或減慢JVM,因為它需要做額外的工作來識別運算子的實際含義,並減少最佳化的機會, 以保證Java中運算子的行為。

    2.避免程式設計錯誤。Java不允許使用者定義的運算子過載,因為如果允許程式設計師進行運算子過載,將為同一運算子賦予多種含義,這將使任何開發人員的學習曲線變得陡峭,事情變得更加混亂。據觀察,當語言支援運算子過載時,程式設計錯誤會增加,從而增加了開發和交付時間。由於Java和JVM已經承擔了大多數開發人員的責任,如在透過提供垃圾收集器進行記憶體管理時,因為這個功能增加汙染程式碼的機會,成為程式設計錯誤之源,因此沒有多大意義。

    3.JVM複雜性。從JVM的角度來看,支援運算子過載使問題變得更加困難。透過更直觀,更乾淨的方式使用方法過載也能實現同樣的事情,因此不支援Java中的運算子過載是有意義的。與相對簡單的JVM相比,複雜的JVM可能導致JVM更慢,併為保證在Java中運算子行為的確定性從而減少了最佳化程式碼的機會。

    4.讓開發工具處理更容易。這是在Java中不支援運算子過載的另一個好處。省略運算子過載使語言更容易處理,這反過來又更容易開發處理語言的工具,例如IDE或重構工具。Java中的重構工具遠勝於C++。

  • 3 # 慎談奧秘

    C# 可過載和不可過載運算子

    【一】可過載運算子 說明

    +, -, !, ~, ++, -- 這些一元運算子只有一個運算元,且可以被過載

    +, -, *, /, % 這些二元運算子帶有兩個運算元,且可以被過載

    ==, !=, <, >, <=, >= 這些比較運算子可以被過載

    【二】

    不可過載運算子 說明

    &&, || 這些條件邏輯運算子不能被直接過載

    +=, -=, *=, /=, %= 這些賦值運算子不能被過載

    =, ., ?:, ->, new, is, sizeof, typeof

    -----------------河南新華

  • 4 # I沒有T

    1) 簡單性和清晰性。

    清晰性是 Java 設計者的目標之一。設計者不是隻想複製語言,而是希望擁有一種清晰,真正面向物件的語言。新增運算子過載比沒有它肯定會使設計更復雜,並且它可能導致更復雜的編譯器, 或減慢 JVM,因為它需要做額外的工作來識別運算子的實際含義,並減少最佳化的機會, 以保證 Java 中運算子的行為。

    2) 避免程式設計錯誤。

    Java 不允許使用者定義的運算子過載,因為如果允許程式設計師進行運算子過載,將為同一運算子賦予多種含義,這將使任何開發人員的學習曲線變得陡峭,事情變得更加混亂。

    據觀察,當語言支援運算子過載時,程式設計錯誤會增加,從而增加了開發和交付時間。由於 Java和 JVM 已經承擔了大多數開發人員的責任,如在透過提供垃圾收集器進行記憶體管理時,因為這個功能增加汙染程式碼的機會, 成為程式設計錯誤之源, 因此沒有多大意義。

    3) JVM複雜性。

    從JVM的角度來看,支援運算子過載使問題變得更加困難。透過更直觀,更乾淨的方式使用方法過載也能實現同樣的事情,因此不支援 Java 中的運算子過載是有意義的。與相對簡單的 JVM 相比,複雜的 JVM 可能導致 JVM 更慢,併為保證在 Java 中運算子行為的確定性從而減少了最佳化程式碼的機會。

    4) 讓開發工具處理更容易。

    這是在 Java 中不支援運算子過載的另一個好處。省略運算子過載後使語言更容易處理,如靜態分析等,這反過來又更容易開發處理語言的工具,例如 IDE 或重構工具。Java 中的重構工具遠勝於 C++。

  • 5 # 使用者5663516205676

    1)簡單性和清晰性。清晰性是Java設計者的目標之一。設計者不是隻想複製語言,而是希望擁有一種清晰,真正面向物件的語言。新增運算子過載比沒有它肯定會使設計更復雜,並且它可能導致更復雜的編譯器, 或減慢 JVM,因為它需要做額外的工作來識別運算子的實際含義,並減少最佳化的機會, 以保證 Java 中運算子的行為。

    2)避免程式設計錯誤。Java 不允許使用者定義的運算子過載,因為如果允許程式設計師進行運算子過載,將為同一運算子賦予多種含義,這將使任何開發人員的學習曲線變得陡峭,事情變得更加混亂。據觀察,當語言支援運算子過載時,程式設計錯誤會增加,從而增加了開發和交付時間。由於 Java 和 JVM 已經承擔了大多數開發人員的責任,如在透過提供垃圾收集器進行記憶體管理時,因為這個功能增加汙染程式碼的機會, 成為程式設計錯誤之源, 因此沒有多大意義。

    3)JVM複雜性。從JVM的角度來看,支援運算子過載使問題變得更加困難。透過更直觀,更乾淨的方式使用方法過載也能實現同樣的事情,因此不支援 Java 中的運算子過載是有意義的。與相對簡單的 JVM 相比,複雜的 JVM 可能導致 JVM 更慢,併為保證在 Java 中運算子行為的確定性從而減少了最佳化程式碼的機會。

    4)讓開發工具處理更容易。這是在 Java 中不支援運算子過載的另一個好處。省略運算子過載使語言更容易處理,這反過來又更容易開發處理語言的工具,例如 IDE 或重構工具。Java 中的重構工具遠勝於 C++。

  • 6 # 匯姐聊程式語言

    Java不支援運算子過載,具體為什麼,我可能說的不太完美,但Java就是這麼實用,熱門,

    與C++、C#不同,不支援多重繼承,沒有指標,沒有引用傳遞,就少了很多功能,下面我講一下Java不支援運算子過載的優點,實用性。

    一、清晰性。

    清晰性是Java設計者的目標之一。設計者是希望有一種清晰,真正面向物件的語言。如果有運算子過載的話,肯定會比沒有運算子過載設計更加複雜,甚至會減慢編譯器的速度。

    二、可以有效避免程式錯誤。

    用運算子過載的話,可能會使整個專案的開發人員處於特別緊張的狀態,運算子過載將為同一運算子賦予多種含義,這樣會增加很多程式設計錯誤,可以說這樣會增加整個專案的難度,會讓開發人員陷入一種無奈,到最後要去學習,去研究導致最後交付不了。因此沒有太大的意義,所以這點好處是有的。

    三、讓開發處理更容易。

    and

    可能很多人用過C++,就會發現,其實運算子過載也挺方便,但是在JAVA中就不這樣用,JAva的目標之一就是清晰性。

  • 7 # Java大資料高階架構師

    Java的確不支援運算子過載,然後歷史原因覺得和Java起初的設計思想有關(嚴格的“OOP”思想),然後反駁下樓上說Java裡面的String可以使用“+”進行連線的問題,藐視看起來像是過載了運算子,其實這個只是JVM做的語法糖。來,看下這個例子。

    編譯後生成Example.class,然後咱們使用

    看到效果如下::

    是不是發現多了些什麼?沒錯,JVM編譯的時候自動給你搞出了個StringBuilder,然後invokevirtual呼叫了下生成的StringBuilder物件的append方法,緊接著又調了toString方法。。。剩下自己腦補吧,反正就這麼幾句,把Constant pool也截圖給你:

    所以說啊,這個String的“+”運算子並沒有進行什麼過載,這個根本就是一個語法糖。

    要說為什麼Java不支援運算子過載,我覺得樓上說的很好 (引用下)-----------------“個人覺得從專案管理和程式碼質量的角度來看,運算子過載可能導致程式碼的可讀性降低,不宜維護。Java中去掉這個特性也沒什麼不好的。”的確,個人也認為(引用自網路)----“運算子過載增加了複雜性,這與JAVA最初的設計思想不符,它並不實用,反而增加了程式的閱讀性”。嚴格的“OOP”思想可能是這個問題的歷史原因。

  • 8 # 後婲緣

    當有人把我們常識記憶的運算子過載的亂七八糟,然後把這個程式碼藏起來,在常用的地方寫幾個超長的表示式,你就知道為啥不過載運算子了,寫得好並不等於寫出讓人無法理解的程式碼,應試教育喜歡這麼玩,搞點多重指標,過載,讓考生摸不著頭腦,然而真正的好專案並不是智力遊戲,很多好的專案使用起來非常的舒服簡單,安卓的黃油刀,picasso,等優秀的開源專案寫起來簡單流暢,原始碼讀起來通俗易懂,這才是真的厲害。

  • 9 # Will味

    許多大能對java不支援運算子過載的原因進行了分析。就我個人理解,不管java設計者當初出於什麼樣的考慮,這一決策的一個明顯的好處是使得程式易於理解!為什麼?

    運算子本身具有其預設的、確定的運算含義,而運算子過載使得運算子的運算功能具有了不確定性,從而使得基於這種不確定性的包含過載運算子的程式程式碼具有了不確定性!

    也就是說,要想理解包含過載運算子的程式程式碼的確切含義,首先要找到運算子被過載的地方,理解過載後運算子的含義!運算子被過載了嗎?在哪裡過載的?過載後它是什麼意思?帶有這些疑問去閱讀程式會相當累,還不如直接了當用函式來表達相應功能更顯得簡單!

    另外,允許運算子過載在某種程度上違背了開閉設計原則。運算子的過載和使用不是區域性化的,過載可能發生在多處,使用的地方可能會更多。對某一過載程式碼的修改,其影響範圍存在某種程度的不確定性!

    總之,運算子過載有其優點,對於追求形式美的人來說具有極大的誘惑力!然而我們大多數人是現實的!java不支援運算子過載,我認為更多的是現實的考量!!!

  • 中秋節和大豐收的關聯?
  • 如何做好一份前端工程師的簡歷呢?