In Java, is it ever a bad idea to make an object&#

2020-04-05 06:57发布

问题:

I have a data class in my application. My application will never be used as a public API and I will be the only person developing code within my project.

I am trying to save every ounce of processor and memory power I can.

Is it a bad idea to make my data members in my data class to have public/protected/default protection so that I don't have to use a getter? Using a getter would require slightly more memory and creation of a stack and such...which I believe isn't necessary. The only reason I can see in using a getter is for protection/privacy, but if I'm the only coder and nobody else is going to be using my API, then is it a bad idea not to use the getters?

Please let me know if this is stupid.

回答1:

If you are replacing getters/setters for performance/memory optimisation, then you are taking the wrong approach. These are almost certainly not going to be the reason your application runs slowly or uses too much memory.

The cardinal sin of optimisation is to do it before you know that you need to. Optimise only when you have real information showing you where the most time/memory is being wasted, and then spend your time optimising that. The thinking behind this is that you will gain more by shaving off 5% of time in a piece of code that takes up 80% of the total run time, than shaving off even 20% in a piece of code that contributes only 5% to the total run time. (Same applies to memory).

Also, I would be careful about designing the application as you suggest, since it will mean that some properties (eg: simple properties) will be directly accessible, while others (more complex derived properties, or properties where you do not want to expose the underlying types) will have getters/setters. So you will end up with a mix of access styles, which will be less maintainable.



回答2:

It is a bad idea to make members publicly available as an optimization. As others have said, it will have virtually no impact. However, if the object is a simple data structure with little behavior, it's fine to write it that way. As long as you're doing it for simplicity and readability, not performance.



回答3:

Chances are good that trivial getters and setters will be inlined anyway - but with public fields you'll have lost the distinction between contract (API) and implementation. Even if this is only an API that you will use, it's good to keep things loosely coupled IMO.



回答4:

Python folks don't use getters and they're not burning in hell.

Getter/setter is a way to expose part of the class to Java's simple introspection. The Java Bean specification relies on having public getters and setters as a way to determine which attributes are important.

While essential for things that require/produce/use beans, it is not essential feature of OO programming or Java programming. It's just part of the Bean spec, and is required for any class that wants to participate in bean-like things.

Public attributes are fine. They're simple, direct and obvious.



回答5:

As with any optimisation, measure before and after to see whether there is any benefit that justifies the downsides.

I think that you will find that it won't make any noticeable difference to the performance of your code (but try it for yourself). The JVM will inline frequently used getters and setters.

Use a profiler and find the real hotpsots in your application. Optimisation without evidence is just guesswork.



回答6:

This is slightly heretical, but I agree with you that if you know nobody else is going to be using your class, you can skip that stuff. I wouldn't do it in code that might be reused, even hidden behind an API, but in your case it seems reasonably safe.



回答7:

I'm pretty sure the amount of memory used is negligible. The JVM might even optimize the calls to make them inline depending on the implementation at runtime if you build for production. If you are developing it yourself, it might still help because if you have a difficult to track bug you can set a breakpoint in the getter/setter and see exactly when you are changing the values. It shouldn't be any code to write either because most modern IDE's have functions to generate the code automatically.



回答8:

All the premature optimization issues have been discussed in the previous answers.

I just thought it's worth mentioning that in Java you can imitate C's structs by defining a class with only public members like so:

public class DataClass {
    public int a;
    public String b;
    public char c;
}

and which will be accessed (getting and setting) only by referring to those public members.

This idiom is totally acceptable in some specific scenarios.



回答9:

Getters and setters are not just for protection/privacy. The main use is to change the way you expose the underlying data.

For example, in asp.net c# the visible property of a control may have something such as:

private bool visible = true;

public bool Visible
{
    get
    {
        return visible && Parent.Visible;
    }
    set
    {
        visible = value;
    }
}

The external property (accessor/mutator) has a different meaning to the underlying variable. In addition, if you don't have the above functionality when you begin, when you have simple gets / sets it becomes something that you can add at a later date, easily.

As for the performance point and saving every ounce of processor and memory you can. If this is going to make a noticeable impact then you're probably better off looking at a different language.



回答10:

I've never wished I'd had a public field instead of a property, but I can't count the number of times I've wanted a property and had a public field. Eventually you end up wanting logic inside it.



回答11:

I think the inverse of this question is a better one to ask:

Is it ever a good idea to make an objects members publicly available?

With development, it's generally a good idea to approach things from the principle of using the lowest accessibility level required; in other words, only expose as much data as you need. If you apply this principle generally, then it becomes more of a practice of only sharing as much as is required.

So with that said - why do this?



回答12:

Consider that once you put something in a public API that it is set in stone. You cannot change the name of it, you cannot change the type of it, you cannot change how it is stored.

Putting it behind a method results in no performance penelty on a modern VM (pretty much anything in the last 10 years).

The flexibility that you gain from having methods to get the value is more important in my opinion,.

If you do decide to make them public make sure that you mark them as final and that they are immutable.

public class Point
{
    public final int x;
    public final int y;

    public Point(final int xVal, final int yVal) 
    {
        x = xVal;
        y = yVal;
    }
}

Edit:

By public API I mean any variable that is public, not that the class itself would be part of the exposed API for other developers. By making it public you can cause yourself issues later on (if it were a "real" public API that other developers had access to it is literally set in stone unless you like breaking other peoples code when you release an update).



回答13:

It's not stupid, but I'm thinking it's probably not a good idea either.

I understand the temptation...if no one else is ever using the code, then you don't need to worry about it. But time and time again, it turns out this just doesn't match with what actually happens...you find out that you'll be using it in more places than you thought, or the project becomes bigger than expected, or someone just gets ahold of it and thinks it's useful...things that were never meant to be permenant or public have a way of becoming that way.

Is there really that tight a limit on processor power and memory? I don't know about your situation (embedded app, perhaps?), but as an (overgeneralized) rule, you're better off looking elsewhere for resources...in your data structures, algorithms, or archetecture to see better improvements in memory or CPU.



回答14:

You never know, always code as if the people who read your code know where you live and are serial killers. Even if you develop code only for yourself, try (imo) to do it as if you were developing in a team. This way you get used to build readable best-practice code. As of for the performance overhead, maybe it is better to do it in something like C then if you really really really need every bit of your memory and every cycle of your CPU.



回答15:

IMHO, for properties that by their very nature are just meant to passively hold data and couldn't do anything more complicated without completely changing their meaning from the perspective of the API user, getters and setters are just unnecessary boilerplate. If the property is, and by its nature clearly always will be, just a data holder, such as a class that's used similarly to a C struct, just make the stupid thing public. As a more general statement, best practices can easily become a waste of time or even worst practices when applied overzealously without thinking about why they're best practices. Heck, even goto is arguably justified once in a while.



回答16:

Potential drawbacks for using

public final type var;

instead of

private final type var;

public type getvar() {return var ;}

(which I've personally experienced) include:

  • The prospect -- however unlikely it seems now -- that the representation will need to change, in the base class or in some future subclass.

  • The potential awkwardness of mixing exposed immutably- and mutable-typed fields.

  • The bother of remembering which classes use public final and which use getvar .

  • The bother of explaining this inconsistency to someone else who may later see the source code.

I've just never been able to convince myself that it's worth it, even though I've tried.



回答17:

I have create two small classes, and test them.

  • First I've create a single object as prototype.

  • Then I've create a 2,000,000 array to store them all.

  • Then I've run a loop, create a new instance there and take the values from the prototype.

The average results in seconds comparing each one is:

WithGS  Without
1.1323  1.1116

Diff  = 0.0207 secs.

So, for this case I think it would be much better to have a non-optimized solution first, and once no further requirements are needed, proceed with the profiling.

Here's the code:

PersonGetSet.java


public class PersonGetSet {
    private String name;
    private boolean deceased;

    public void setName( String name ) {
        this.name = name;
    }
    public String getName() {
        return name;
    }

    public void setDeceased( boolean deceased ) {
        this.deceased = deceased;
    }
    public boolean isDeceased() {
        return this.deceased;
    }

    public static void main( String [] args )  {
        PersonGetSet pb = new PersonGetSet();
        pb.setName( "name" );
        pb.setDeceased( true ) ;

        long start = System.currentTimeMillis();
        PersonGetSet [] array = new PersonGetSet[2000000];
        for( int i = 0 ; i < array.length; i++ ) {
            PersonGetSet personGs = new PersonGetSet();
            personGs.setName( pb.getName() );
            personGs.setDeceased( pb.isDeceased() );
            array[i] =  personGs;
        }
        System.out.println( "PersonGetSet took " + ( System.currentTimeMillis() - start ) + " ms. " );
    }
}


Person.java


public class Person {
    String name;
    boolean deceased;
    public static void main( String [] args )  {
        Person pb = new Person();
        pb.name=  "name" ;
        pb.deceased = true;

        long start = System.currentTimeMillis();
        Person [] array = new Person[2000000];
        for( int i = 0 ; i < array.length; i++ ) {
            Person simplePerson = new Person();
            simplePerson.name=  pb.name;
            simplePerson.deceased = pb.deceased;
            array[i] =  simplePerson;
        }
        System.out.println( "Person took " + ( System.currentTimeMillis() - start ) + " ms. " );
    }
}


回答18:

If your priority is performance, you should look into your choice of Java Virtual Machine and how it executes programs. The Desktop Java from Sun spends a LOT of time profiling and compiling java byte code to machine code which results in programs which may run very fast but uses a lot of memory.

If you find that you want to use a HotSpot based JVM, you should look into all the tricks to help it. Getters and setters are usually inlined (i.e. replaced directly in the machine code, instead of a call to a subroutine) and constants are folded. Switch statements for small ranges are usually converted to a jump table.

Normally I find that the "head-under-the-arm" method works well for writing most of the code, and then you profile the running code and find the bottlenecks. I found for instance that StringBuffers are fast for many things, but deleteCharAt(0) is not one of them. Rewriting to use a simple iteration over the string instead of deleting in the string buffer, did wonders. You too, will most likely find that the largest benefits are in algorithms - not small shortcuts.

In the C world the human can hardly outsmart the compiler anymore. In the Java world, the human can help HotSpot by writing straight forward Java.



回答19:

Any stack use is going to be highly transitory and not a certainty anyway. You may find the compiler optimizes out any unnecessary overhead anyway so you may find you gain nothing in real terms.

I agree it's not necessarily bad for a private non-API class to use public members but what you gain is so small (if there's any at all) I just wouldn't bother.



回答20:

From the perspective of who has written the code and who is going to use it, then it doesn't matter whether its you or anyone else, you might still need getters and setters - if anyone at all uses it later on its a defacto public API.

However, if it really is just data and you don't need any behaviour associated with the class, then by all means have the members public.

To me its whether the class has any behaviour or if you might want to change the names or types of its data members as implementation details that really matters.

As to processor power and memory - have you measured?



回答21:

If speed is that critical, it might be worth writing it in C or C++.



回答22:

Designing functionally you are never going to need (getters/setters) is just as bad as premature optimisation. If this is really just for your use, don't make it public and then structure it which you believe is the simplest.

Optimisation and/or getters should only be added if you actually need them.



回答23:

Do you need the getter at all ? That is to say, should your object be doing work for you, rather than your client getting data values and performing work themselves ?

One key aspect of OOP is telling an object to do something, instead of pulling the bits out and doing it yourself. e.g

double ret = myObj.calculateReturn()

vs.

int v = myObj.getValue();
int ov = myObj.getOldValue();
double ret = (v-ov)/ov * 100; // do I work about dividing by zero etc.? 

which I see a lot. Often you do need getters (and setters, perhaps), but when I write a getter I always ask myself whether that data should be exposed, or whether the object should hide it completely for me.



回答24:

It's always a bad idea to make non-final fields public.

What if you later want to add event notification?

What if you later want to subclass and change the behavior?

What if there are some cross-field constraints you want to enforce?

TofuBeer shows a Point class with final fields -- that's ok. However, the Point class in AWT doesn't use finals, and caused me lots of grief early on in Java.

I was writing some 3D code and wanted to subclass Point such that whenever x & y changed I could automatically adjust z as needed. Because Point's fields were public, there was no way to know when x or y changed!



回答25:

At our team we use the following rule: if the state represented by a class field is mutable then always make it private and provide mutators; however, if it is immutable (i.e. would not be reassigned after class instantiation) then it can be declared as public final [type] [field-name] -- safe for accessing.

Pls note that here "immutable state" specifically means that field is declared as final. Should not be confused with public [immutable-type] [field-name] where one could perform reassignment albeit immutable-type instance is itself immutable.

Pls also note that there are many good reasons to have accessor even for an immutable field. For example, in cases where it is required to perform some action (e.g. security check) at the time when the field is read.



回答26:

The argument that this code is only going to be for your own use is a dead easy trap to fall into - this is the case with most code, the publicly exposed bits are usually very small. So assuming most code is for private use, why bother ever using private, protected, final, const (C/C++) - retorical question I know, but I hope the point is clear.

The other thing that is being missed here is locking. If you access the member directly you will only ever be able to lock this code at the whole object or the member level. And that locking will be in the control of the using code, not the Object itself. Maybe OK, maybe not, as in all things it's horses for courses.



回答27:

If performance is critical, I would suggest making fields either private if only accessed in the same class or package local if they are accessed in another class. If you give an inner/nested class private fields, the compiler will add/use accessor methods (as the VM does not allow classes to access fields in another class even though Java does)

Having public mutable fields suggests a design issue to me. Having performance critical code split across packages doesn't suggest the sort of tight coding you need for performance critical code.

If you have immutable fields, I don't have a problem making the field public provided you expect a higher development cost if the fields change behaviour. In fact I suggest using public immutable fields, if you believe this makes the code simpler/easier to understand.

However the first priority should still be code readability. The simplest code often performs best as well.



回答28:

In general: If you want to optimize something manually, you should consider architectural improvements rather than such microoptimisations. Microoptimistions can be in many cases done automatically by a tool.

For Java: There is ProGuard tool, that can do many optimizations. However, if you use a modern JVM (say HotSpot or OpenJDK), the JVM can do these optimizations Just-In-Time. If you have a long-running application, ProGuard will probably have a minor effect on the performance.